@agent-native/toolkit 0.10.1 → 0.10.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.
- package/dist/chat-history.css +2 -2
- package/dist/composer/PromptComposer.d.ts +0 -1
- package/dist/composer/PromptComposer.d.ts.map +1 -1
- package/dist/composer/PromptComposer.js +1 -27
- package/dist/composer/PromptComposer.js.map +1 -1
- package/dist/composer/PromptComposer.spec.js +1 -8
- package/dist/composer/PromptComposer.spec.js.map +1 -1
- package/dist/composer/RealtimeVoiceMode.d.ts.map +1 -1
- package/dist/composer/RealtimeVoiceMode.js +5 -2
- package/dist/composer/RealtimeVoiceMode.js.map +1 -1
- package/dist/composer/RealtimeVoiceMode.spec.js +9 -0
- package/dist/composer/RealtimeVoiceMode.spec.js.map +1 -1
- package/dist/design-system/design-system.spec.js +47 -2
- package/dist/design-system/design-system.spec.js.map +1 -1
- package/dist/editor/SharedRichEditor.d.ts +3 -1
- package/dist/editor/SharedRichEditor.d.ts.map +1 -1
- package/dist/editor/SharedRichEditor.js +17 -3
- package/dist/editor/SharedRichEditor.js.map +1 -1
- package/dist/editor/SharedRichEditor.spec.d.ts +2 -0
- package/dist/editor/SharedRichEditor.spec.d.ts.map +1 -0
- package/dist/editor/SharedRichEditor.spec.js +63 -0
- package/dist/editor/SharedRichEditor.spec.js.map +1 -0
- package/dist/editor/SlashCommandMenu.d.ts +10 -0
- package/dist/editor/SlashCommandMenu.d.ts.map +1 -1
- package/dist/editor/SlashCommandMenu.js +54 -2
- package/dist/editor/SlashCommandMenu.js.map +1 -1
- package/dist/editor/SlashCommandMenu.spec.js +48 -1
- package/dist/editor/SlashCommandMenu.spec.js.map +1 -1
- package/dist/editor/index.d.ts +1 -1
- package/dist/editor/index.d.ts.map +1 -1
- package/dist/editor/index.js +1 -1
- package/dist/editor/index.js.map +1 -1
- package/dist/editor.css +75 -0
- package/dist/styles.css +183 -36
- package/dist/ui/button.d.ts.map +1 -1
- package/dist/ui/button.js +2 -2
- package/dist/ui/button.js.map +1 -1
- package/package.json +1 -1
- package/src/chat-history.css +2 -2
- package/src/composer/PromptComposer.spec.ts +1 -12
- package/src/composer/PromptComposer.tsx +0 -31
- package/src/composer/RealtimeVoiceMode.spec.tsx +14 -0
- package/src/composer/RealtimeVoiceMode.tsx +7 -2
- package/src/design-system/design-system.spec.tsx +99 -1
- package/src/editor/SharedRichEditor.spec.tsx +82 -0
- package/src/editor/SharedRichEditor.tsx +23 -2
- package/src/editor/SlashCommandMenu.spec.ts +57 -0
- package/src/editor/SlashCommandMenu.tsx +68 -2
- package/src/editor/index.ts +1 -0
- package/src/editor.css +75 -0
- package/src/styles.css +183 -36
- package/src/ui/button.tsx +3 -2
|
@@ -6,6 +6,7 @@ import type { Doc as YDoc } from "yjs";
|
|
|
6
6
|
|
|
7
7
|
import { cn } from "../utils.js";
|
|
8
8
|
import { BubbleToolbar, type BubbleToolbarItem } from "./BubbleToolbar.js";
|
|
9
|
+
import { DragHandle } from "./DragHandle.js";
|
|
9
10
|
import {
|
|
10
11
|
createSharedEditorExtensions,
|
|
11
12
|
type RichMarkdownDialect,
|
|
@@ -37,6 +38,8 @@ export interface SharedRichEditorProps {
|
|
|
37
38
|
preset?: RichMarkdownEditorPreset;
|
|
38
39
|
/** Toggle individual base extensions (tables/tasks/link/codeBlock/image). */
|
|
39
40
|
features?: SharedEditorFeatures;
|
|
41
|
+
/** Show the Notion-style block grip and action menu. Defaults to `true`. */
|
|
42
|
+
dragHandle?: boolean;
|
|
40
43
|
/**
|
|
41
44
|
* Injectable image uploader for the shared image block. Used only when
|
|
42
45
|
* `features.image` is on. Pass `uploadEditorImage` (the framework
|
|
@@ -141,6 +144,7 @@ export function SharedRichEditor({
|
|
|
141
144
|
dialect = "gfm",
|
|
142
145
|
preset = "plan",
|
|
143
146
|
features,
|
|
147
|
+
dragHandle = true,
|
|
144
148
|
onImageUpload = null,
|
|
145
149
|
extraExtensions,
|
|
146
150
|
placeholder = "Type '/' for commands...",
|
|
@@ -170,6 +174,23 @@ export function SharedRichEditor({
|
|
|
170
174
|
onChangeRef.current = onChange;
|
|
171
175
|
onBlurRef.current = onBlur;
|
|
172
176
|
|
|
177
|
+
const effectiveExtraExtensions = useMemo(() => {
|
|
178
|
+
const extras = extraExtensions ?? [];
|
|
179
|
+
if (
|
|
180
|
+
!dragHandle ||
|
|
181
|
+
extras.some((extension) => extension.name === "dragHandle")
|
|
182
|
+
) {
|
|
183
|
+
return extras;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return [
|
|
187
|
+
DragHandle.configure({
|
|
188
|
+
wrapperSelector: ".an-rich-md-wrapper",
|
|
189
|
+
}),
|
|
190
|
+
...extras,
|
|
191
|
+
];
|
|
192
|
+
}, [dragHandle, extraExtensions]);
|
|
193
|
+
|
|
173
194
|
const extensions = useMemo(
|
|
174
195
|
() =>
|
|
175
196
|
createSharedEditorExtensions({
|
|
@@ -177,7 +198,7 @@ export function SharedRichEditor({
|
|
|
177
198
|
preset,
|
|
178
199
|
placeholder,
|
|
179
200
|
features,
|
|
180
|
-
extraExtensions,
|
|
201
|
+
extraExtensions: effectiveExtraExtensions,
|
|
181
202
|
onImageUpload,
|
|
182
203
|
collab: ydoc ? { ydoc, awareness, user } : null,
|
|
183
204
|
disableHistory,
|
|
@@ -192,7 +213,7 @@ export function SharedRichEditor({
|
|
|
192
213
|
placeholder,
|
|
193
214
|
preset,
|
|
194
215
|
features,
|
|
195
|
-
|
|
216
|
+
effectiveExtraExtensions,
|
|
196
217
|
onImageUpload,
|
|
197
218
|
ydoc,
|
|
198
219
|
awareness,
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import { Editor } from "@tiptap/core";
|
|
4
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
1
5
|
import { describe, expect, it } from "vitest";
|
|
2
6
|
|
|
3
7
|
import {
|
|
4
8
|
DEFAULT_SLASH_COMMANDS,
|
|
5
9
|
filterSlashCommandItems,
|
|
10
|
+
focusEditorInInsertedBlock,
|
|
6
11
|
} from "./SlashCommandMenu.js";
|
|
7
12
|
|
|
8
13
|
describe("filterSlashCommandItems", () => {
|
|
@@ -31,3 +36,55 @@ describe("filterSlashCommandItems", () => {
|
|
|
31
36
|
);
|
|
32
37
|
});
|
|
33
38
|
});
|
|
39
|
+
|
|
40
|
+
describe("focusEditorInInsertedBlock", () => {
|
|
41
|
+
it.each([
|
|
42
|
+
["Text", "paragraph", "paragraph"],
|
|
43
|
+
["Heading 1", "heading", "heading"],
|
|
44
|
+
["Bulleted list", "bulletList", "paragraph"],
|
|
45
|
+
["Numbered list", "orderedList", "paragraph"],
|
|
46
|
+
["Quote", "blockquote", "paragraph"],
|
|
47
|
+
["Code block", "codeBlock", "codeBlock"],
|
|
48
|
+
])(
|
|
49
|
+
"leaves the caret inside the %s block instead of after it",
|
|
50
|
+
(title, activeNode, parentNode) => {
|
|
51
|
+
const element = document.createElement("div");
|
|
52
|
+
document.body.appendChild(element);
|
|
53
|
+
const editor = new Editor({
|
|
54
|
+
element,
|
|
55
|
+
extensions: [StarterKit],
|
|
56
|
+
content: {
|
|
57
|
+
type: "doc",
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
type: "paragraph",
|
|
61
|
+
content: [{ type: "text", text: "/code" }],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const slashRange = { from: 1, to: 6 };
|
|
69
|
+
editor.commands.setTextSelection(slashRange);
|
|
70
|
+
editor.chain().focus().deleteRange(slashRange).run();
|
|
71
|
+
const command = DEFAULT_SLASH_COMMANDS.find(
|
|
72
|
+
(item) => item.title === title,
|
|
73
|
+
);
|
|
74
|
+
if (!command) throw new Error(`Missing slash command: ${title}`);
|
|
75
|
+
command.action(editor);
|
|
76
|
+
focusEditorInInsertedBlock(editor, slashRange.from);
|
|
77
|
+
|
|
78
|
+
expect(editor.isActive(activeNode)).toBe(true);
|
|
79
|
+
expect(editor.state.selection.$from.parent.type.name).toBe(parentNode);
|
|
80
|
+
expect(editor.state.selection.from).toBeLessThanOrEqual(
|
|
81
|
+
editor.state.selection.$from.end(),
|
|
82
|
+
);
|
|
83
|
+
expect(editor.isFocused).toBe(true);
|
|
84
|
+
} finally {
|
|
85
|
+
editor.destroy();
|
|
86
|
+
element.remove();
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TextSelection } from "@tiptap/pm/state";
|
|
1
2
|
import type { Editor } from "@tiptap/react";
|
|
2
3
|
import {
|
|
3
4
|
useCallback,
|
|
@@ -27,6 +28,66 @@ export interface SlashCommandItem {
|
|
|
27
28
|
action: (editor: Editor) => void;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Put the caret inside the block a slash command just created or transformed.
|
|
33
|
+
*
|
|
34
|
+
* Tiptap commands usually preserve a text selection, but commands that replace
|
|
35
|
+
* the current paragraph (notably code blocks and horizontal rules) can leave a
|
|
36
|
+
* node selection or a caret at the following block. Resolving from the slash
|
|
37
|
+
* range keeps the behavior consistent for built-in and app-provided block
|
|
38
|
+
* commands: after selecting a block, typing starts in that block immediately.
|
|
39
|
+
*/
|
|
40
|
+
export function focusEditorInInsertedBlock(
|
|
41
|
+
editor: Editor,
|
|
42
|
+
anchorPosition?: number,
|
|
43
|
+
): void {
|
|
44
|
+
if (editor.isDestroyed) return;
|
|
45
|
+
|
|
46
|
+
const { state } = editor;
|
|
47
|
+
const anchor = Math.max(
|
|
48
|
+
1,
|
|
49
|
+
Math.min(anchorPosition ?? state.selection.from, state.doc.content.size),
|
|
50
|
+
);
|
|
51
|
+
let containingTextblock: number | null = null;
|
|
52
|
+
let nextTextblock: number | null = null;
|
|
53
|
+
let previousTextblock: number | null = null;
|
|
54
|
+
|
|
55
|
+
state.doc.nodesBetween(0, state.doc.content.size, (node, pos) => {
|
|
56
|
+
if (!node.isTextblock) return;
|
|
57
|
+
|
|
58
|
+
const start = pos + 1;
|
|
59
|
+
const end = pos + node.nodeSize - 1;
|
|
60
|
+
if (anchor >= start && anchor <= end) {
|
|
61
|
+
containingTextblock ??= anchor;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (start > anchor) {
|
|
65
|
+
if (nextTextblock === null || start < nextTextblock) {
|
|
66
|
+
nextTextblock = start;
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (previousTextblock === null || start > previousTextblock) {
|
|
71
|
+
previousTextblock = Math.min(anchor, end);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const target = containingTextblock ?? nextTextblock ?? previousTextblock;
|
|
76
|
+
if (target === null) {
|
|
77
|
+
editor.commands.focus();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const transaction = state.tr.setSelection(
|
|
82
|
+
TextSelection.create(
|
|
83
|
+
state.doc,
|
|
84
|
+
Math.max(1, Math.min(target, state.doc.content.size)),
|
|
85
|
+
),
|
|
86
|
+
);
|
|
87
|
+
editor.view.dispatch(transaction.scrollIntoView());
|
|
88
|
+
editor.view.focus();
|
|
89
|
+
}
|
|
90
|
+
|
|
30
91
|
export interface SlashCommandFeatureFlags {
|
|
31
92
|
tables?: boolean;
|
|
32
93
|
tasks?: boolean;
|
|
@@ -192,13 +253,18 @@ export function SlashCommandMenu({
|
|
|
192
253
|
(command: SlashCommandItem) => {
|
|
193
254
|
if (slashPosRef.current !== null) {
|
|
194
255
|
const { from } = editor.state.selection;
|
|
256
|
+
const anchorPosition = slashPosRef.current;
|
|
195
257
|
editor
|
|
196
258
|
.chain()
|
|
197
259
|
.focus()
|
|
198
|
-
.deleteRange({ from:
|
|
260
|
+
.deleteRange({ from: anchorPosition, to: from })
|
|
199
261
|
.run();
|
|
262
|
+
command.action(editor);
|
|
263
|
+
focusEditorInInsertedBlock(editor, anchorPosition);
|
|
264
|
+
} else {
|
|
265
|
+
command.action(editor);
|
|
266
|
+
focusEditorInInsertedBlock(editor);
|
|
200
267
|
}
|
|
201
|
-
command.action(editor);
|
|
202
268
|
close();
|
|
203
269
|
},
|
|
204
270
|
[close, editor],
|
package/src/editor/index.ts
CHANGED
package/src/editor.css
CHANGED
|
@@ -8,6 +8,81 @@
|
|
|
8
8
|
cursor: default;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/* Shared Notion-style block controls. Apps can override the color tokens, but
|
|
12
|
+
every SharedRichEditor gets a usable grip/menu without app-local CSS. */
|
|
13
|
+
.drag-handle {
|
|
14
|
+
position: absolute;
|
|
15
|
+
display: none;
|
|
16
|
+
width: 24px;
|
|
17
|
+
height: 24px;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
border-radius: 4px;
|
|
21
|
+
color: hsl(var(--muted-foreground) / 0.45);
|
|
22
|
+
cursor: grab;
|
|
23
|
+
user-select: none;
|
|
24
|
+
touch-action: none;
|
|
25
|
+
z-index: 10;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.drag-handle:hover,
|
|
29
|
+
.drag-handle:focus-visible {
|
|
30
|
+
background: hsl(var(--muted));
|
|
31
|
+
color: hsl(var(--muted-foreground));
|
|
32
|
+
outline: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.drag-handle:active {
|
|
36
|
+
cursor: grabbing;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.notion-editor-is-dragging,
|
|
40
|
+
.notion-editor-is-dragging * {
|
|
41
|
+
cursor: grabbing !important;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.notion-block--dragging {
|
|
45
|
+
opacity: 0.35;
|
|
46
|
+
transition: opacity 0.12s ease;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.notion-drag-preview {
|
|
50
|
+
position: fixed;
|
|
51
|
+
top: 0;
|
|
52
|
+
left: 0;
|
|
53
|
+
z-index: 9999;
|
|
54
|
+
max-width: min(720px, calc(100vw - 48px));
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
background: transparent !important;
|
|
57
|
+
box-shadow: none;
|
|
58
|
+
opacity: 0.55;
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
will-change: transform;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.notion-drag-preview > * {
|
|
64
|
+
margin: 0 !important;
|
|
65
|
+
max-height: 180px;
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.notion-drag-preview,
|
|
70
|
+
.notion-drag-preview * {
|
|
71
|
+
background: transparent !important;
|
|
72
|
+
background-color: transparent !important;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.notion-drop-indicator {
|
|
76
|
+
position: absolute;
|
|
77
|
+
z-index: 60;
|
|
78
|
+
height: 3px;
|
|
79
|
+
border-radius: 999px;
|
|
80
|
+
background: hsl(var(--ring));
|
|
81
|
+
box-shadow: 0 0 0 1px hsl(var(--background));
|
|
82
|
+
pointer-events: none;
|
|
83
|
+
transform: translateY(-1px);
|
|
84
|
+
}
|
|
85
|
+
|
|
11
86
|
.an-rich-md-loading {
|
|
12
87
|
min-height: 7rem;
|
|
13
88
|
}
|
package/src/styles.css
CHANGED
|
@@ -20,66 +20,213 @@
|
|
|
20
20
|
*/
|
|
21
21
|
@source "./**/*.{js,ts,tsx}";
|
|
22
22
|
|
|
23
|
+
@property --agent-realtime-voice-angle {
|
|
24
|
+
syntax: "<angle>";
|
|
25
|
+
inherits: true;
|
|
26
|
+
initial-value: 0deg;
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
.agent-realtime-voice-glow {
|
|
24
|
-
--agent-realtime-voice-
|
|
25
|
-
--agent-realtime-voice-
|
|
30
|
+
--agent-realtime-voice-angle: 0deg;
|
|
31
|
+
--agent-realtime-voice-proximity: 72;
|
|
32
|
+
--agent-realtime-voice-edge-sensitivity: 30;
|
|
33
|
+
--agent-realtime-voice-color-sensitivity: 50;
|
|
34
|
+
--agent-realtime-voice-cone-spread: 25;
|
|
35
|
+
--agent-realtime-voice-fill-opacity: 0.5;
|
|
36
|
+
--agent-realtime-voice-card-bg: hsl(var(--background));
|
|
37
|
+
--agent-realtime-voice-glow-color: 40 80% 80%;
|
|
38
|
+
--agent-realtime-voice-glow-padding: 18px;
|
|
39
|
+
--agent-realtime-voice-gradient-one: radial-gradient(
|
|
40
|
+
at 80% 55%,
|
|
41
|
+
#c084fc 0,
|
|
42
|
+
transparent 50%
|
|
43
|
+
);
|
|
44
|
+
--agent-realtime-voice-gradient-two: radial-gradient(
|
|
45
|
+
at 69% 34%,
|
|
46
|
+
#f472b6 0,
|
|
47
|
+
transparent 50%
|
|
48
|
+
);
|
|
49
|
+
--agent-realtime-voice-gradient-three: radial-gradient(
|
|
50
|
+
at 8% 6%,
|
|
51
|
+
#38bdf8 0,
|
|
52
|
+
transparent 50%
|
|
53
|
+
);
|
|
54
|
+
--agent-realtime-voice-gradient-four: radial-gradient(
|
|
55
|
+
at 41% 38%,
|
|
56
|
+
#c084fc 0,
|
|
57
|
+
transparent 50%
|
|
58
|
+
);
|
|
59
|
+
--agent-realtime-voice-gradient-five: radial-gradient(
|
|
60
|
+
at 86% 85%,
|
|
61
|
+
#f472b6 0,
|
|
62
|
+
transparent 50%
|
|
63
|
+
);
|
|
64
|
+
--agent-realtime-voice-gradient-six: radial-gradient(
|
|
65
|
+
at 82% 18%,
|
|
66
|
+
#38bdf8 0,
|
|
67
|
+
transparent 50%
|
|
68
|
+
);
|
|
69
|
+
--agent-realtime-voice-gradient-seven: radial-gradient(
|
|
70
|
+
at 51% 4%,
|
|
71
|
+
#f472b6 0,
|
|
72
|
+
transparent 50%
|
|
73
|
+
);
|
|
26
74
|
position: absolute;
|
|
27
75
|
z-index: 0;
|
|
28
|
-
inset: -
|
|
76
|
+
inset: -1px;
|
|
29
77
|
border-radius: inherit;
|
|
78
|
+
isolation: isolate;
|
|
30
79
|
pointer-events: none;
|
|
31
|
-
transform:
|
|
32
|
-
will-change: transform;
|
|
80
|
+
transform: translate3d(0, 0, 0.01px);
|
|
33
81
|
}
|
|
34
82
|
|
|
35
83
|
.agent-realtime-voice-working {
|
|
36
|
-
--agent-realtime-voice-
|
|
37
|
-
--agent-realtime-voice-halo-opacity: 0.58;
|
|
84
|
+
--agent-realtime-voice-proximity: 96;
|
|
38
85
|
}
|
|
39
86
|
|
|
40
87
|
.agent-realtime-voice-glow::before,
|
|
41
88
|
.agent-realtime-voice-glow::after {
|
|
42
89
|
position: absolute;
|
|
43
90
|
inset: 0;
|
|
91
|
+
z-index: 0;
|
|
44
92
|
border-radius: inherit;
|
|
45
93
|
pointer-events: none;
|
|
46
94
|
content: "";
|
|
47
|
-
|
|
48
|
-
from -18deg,
|
|
49
|
-
transparent 0deg 228deg,
|
|
50
|
-
oklch(0.65 0.2 294 / 0.18) 244deg,
|
|
51
|
-
oklch(0.7 0.2 265 / 0.7) 274deg,
|
|
52
|
-
oklch(0.79 0.16 235 / 0.96) 306deg,
|
|
53
|
-
oklch(0.96 0.035 210) 326deg,
|
|
54
|
-
oklch(0.88 0.11 202 / 0.72) 340deg,
|
|
55
|
-
transparent 360deg
|
|
56
|
-
);
|
|
57
|
-
opacity: var(--agent-realtime-voice-edge-opacity);
|
|
58
|
-
padding: 1.5px;
|
|
59
|
-
-webkit-mask:
|
|
60
|
-
linear-gradient(#000 0 0) content-box,
|
|
61
|
-
linear-gradient(#000 0 0);
|
|
62
|
-
mask:
|
|
63
|
-
linear-gradient(#000 0 0) content-box,
|
|
64
|
-
linear-gradient(#000 0 0);
|
|
65
|
-
-webkit-mask-composite: xor;
|
|
66
|
-
mask-composite: exclude;
|
|
67
|
-
transition: opacity 220ms ease-out;
|
|
95
|
+
transition: opacity 250ms ease-out;
|
|
68
96
|
}
|
|
69
97
|
|
|
70
98
|
.agent-realtime-voice-glow::before {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
99
|
+
border: 1px solid transparent;
|
|
100
|
+
background:
|
|
101
|
+
linear-gradient(
|
|
102
|
+
var(--agent-realtime-voice-card-bg),
|
|
103
|
+
var(--agent-realtime-voice-card-bg)
|
|
104
|
+
)
|
|
105
|
+
padding-box,
|
|
106
|
+
linear-gradient(transparent 0 100%) border-box,
|
|
107
|
+
var(--agent-realtime-voice-gradient-one) border-box,
|
|
108
|
+
var(--agent-realtime-voice-gradient-two) border-box,
|
|
109
|
+
var(--agent-realtime-voice-gradient-three) border-box,
|
|
110
|
+
var(--agent-realtime-voice-gradient-four) border-box,
|
|
111
|
+
var(--agent-realtime-voice-gradient-five) border-box,
|
|
112
|
+
var(--agent-realtime-voice-gradient-six) border-box,
|
|
113
|
+
var(--agent-realtime-voice-gradient-seven) border-box,
|
|
114
|
+
linear-gradient(#c084fc 0 100%) border-box;
|
|
115
|
+
opacity: calc(
|
|
116
|
+
(
|
|
117
|
+
var(--agent-realtime-voice-proximity) -
|
|
118
|
+
var(--agent-realtime-voice-color-sensitivity)
|
|
119
|
+
) /
|
|
120
|
+
(100 - var(--agent-realtime-voice-color-sensitivity))
|
|
121
|
+
);
|
|
122
|
+
mask-image: conic-gradient(
|
|
123
|
+
from var(--agent-realtime-voice-angle) at center,
|
|
124
|
+
#000 calc(var(--agent-realtime-voice-cone-spread) * 1%),
|
|
125
|
+
rgb(0 0 0 / 75%) calc((var(--agent-realtime-voice-cone-spread) + 4) * 1%),
|
|
126
|
+
rgb(0 0 0 / 45%) calc((var(--agent-realtime-voice-cone-spread) + 8) * 1%),
|
|
127
|
+
rgb(0 0 0 / 18%) calc((var(--agent-realtime-voice-cone-spread) + 12) * 1%),
|
|
128
|
+
transparent calc((var(--agent-realtime-voice-cone-spread) + 15) * 1%),
|
|
129
|
+
transparent calc((100 - var(--agent-realtime-voice-cone-spread) - 15) * 1%),
|
|
130
|
+
rgb(0 0 0 / 18%)
|
|
131
|
+
calc((100 - var(--agent-realtime-voice-cone-spread) - 12) * 1%),
|
|
132
|
+
rgb(0 0 0 / 45%)
|
|
133
|
+
calc((100 - var(--agent-realtime-voice-cone-spread) - 8) * 1%),
|
|
134
|
+
rgb(0 0 0 / 75%)
|
|
135
|
+
calc((100 - var(--agent-realtime-voice-cone-spread) - 4) * 1%),
|
|
136
|
+
#000 calc((100 - var(--agent-realtime-voice-cone-spread)) * 1%)
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.agent-realtime-voice-glow::after {
|
|
141
|
+
border: 1px solid transparent;
|
|
142
|
+
background:
|
|
143
|
+
var(--agent-realtime-voice-gradient-one) padding-box,
|
|
144
|
+
var(--agent-realtime-voice-gradient-two) padding-box,
|
|
145
|
+
var(--agent-realtime-voice-gradient-three) padding-box,
|
|
146
|
+
var(--agent-realtime-voice-gradient-four) padding-box,
|
|
147
|
+
var(--agent-realtime-voice-gradient-five) padding-box,
|
|
148
|
+
var(--agent-realtime-voice-gradient-six) padding-box,
|
|
149
|
+
var(--agent-realtime-voice-gradient-seven) padding-box,
|
|
150
|
+
linear-gradient(#c084fc 0 100%) padding-box;
|
|
151
|
+
mask-image:
|
|
152
|
+
linear-gradient(to bottom, #000, #000),
|
|
153
|
+
radial-gradient(ellipse at 50% 50%, #000 40%, transparent 65%),
|
|
154
|
+
radial-gradient(ellipse at 66% 66%, #000 5%, transparent 40%),
|
|
155
|
+
radial-gradient(ellipse at 33% 33%, #000 5%, transparent 40%),
|
|
156
|
+
radial-gradient(ellipse at 66% 33%, #000 5%, transparent 40%),
|
|
157
|
+
radial-gradient(ellipse at 33% 66%, #000 5%, transparent 40%),
|
|
158
|
+
conic-gradient(
|
|
159
|
+
from var(--agent-realtime-voice-angle) at center,
|
|
160
|
+
transparent 5%,
|
|
161
|
+
#000 15%,
|
|
162
|
+
#000 85%,
|
|
163
|
+
transparent 95%
|
|
164
|
+
);
|
|
165
|
+
mask-composite: subtract, add, add, add, add, add;
|
|
166
|
+
opacity: calc(
|
|
167
|
+
var(--agent-realtime-voice-fill-opacity) *
|
|
168
|
+
(
|
|
169
|
+
var(--agent-realtime-voice-proximity) -
|
|
170
|
+
var(--agent-realtime-voice-color-sensitivity)
|
|
171
|
+
) /
|
|
172
|
+
(100 - var(--agent-realtime-voice-color-sensitivity))
|
|
173
|
+
);
|
|
174
|
+
mix-blend-mode: soft-light;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.agent-realtime-voice-edge-light {
|
|
178
|
+
position: absolute;
|
|
179
|
+
z-index: 1;
|
|
180
|
+
inset: calc(var(--agent-realtime-voice-glow-padding) * -1);
|
|
181
|
+
border-radius: inherit;
|
|
182
|
+
pointer-events: none;
|
|
183
|
+
opacity: calc(
|
|
184
|
+
(
|
|
185
|
+
var(--agent-realtime-voice-proximity) -
|
|
186
|
+
var(--agent-realtime-voice-edge-sensitivity)
|
|
187
|
+
) /
|
|
188
|
+
(100 - var(--agent-realtime-voice-edge-sensitivity))
|
|
189
|
+
);
|
|
190
|
+
mask-image: conic-gradient(
|
|
191
|
+
from var(--agent-realtime-voice-angle) at center,
|
|
192
|
+
#000 2.5%,
|
|
193
|
+
rgb(0 0 0 / 85%) 4%,
|
|
194
|
+
rgb(0 0 0 / 55%) 7%,
|
|
195
|
+
rgb(0 0 0 / 25%) 11%,
|
|
196
|
+
transparent 15%,
|
|
197
|
+
transparent 85%,
|
|
198
|
+
rgb(0 0 0 / 25%) 89%,
|
|
199
|
+
rgb(0 0 0 / 55%) 93%,
|
|
200
|
+
rgb(0 0 0 / 85%) 96%,
|
|
201
|
+
#000 97.5%
|
|
202
|
+
);
|
|
78
203
|
mix-blend-mode: plus-lighter;
|
|
204
|
+
transition: opacity 250ms ease-out;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.agent-realtime-voice-edge-light::before {
|
|
208
|
+
position: absolute;
|
|
209
|
+
inset: var(--agent-realtime-voice-glow-padding);
|
|
210
|
+
border-radius: inherit;
|
|
211
|
+
content: "";
|
|
212
|
+
box-shadow:
|
|
213
|
+
inset 0 0 0 1px hsl(var(--agent-realtime-voice-glow-color) / 100%),
|
|
214
|
+
inset 0 0 1px hsl(var(--agent-realtime-voice-glow-color) / 60%),
|
|
215
|
+
inset 0 0 3px hsl(var(--agent-realtime-voice-glow-color) / 50%),
|
|
216
|
+
inset 0 0 6px hsl(var(--agent-realtime-voice-glow-color) / 40%),
|
|
217
|
+
inset 0 0 15px hsl(var(--agent-realtime-voice-glow-color) / 30%),
|
|
218
|
+
inset 0 0 25px 2px hsl(var(--agent-realtime-voice-glow-color) / 20%),
|
|
219
|
+
inset 0 0 50px 2px hsl(var(--agent-realtime-voice-glow-color) / 10%),
|
|
220
|
+
0 0 1px hsl(var(--agent-realtime-voice-glow-color) / 60%),
|
|
221
|
+
0 0 3px hsl(var(--agent-realtime-voice-glow-color) / 50%),
|
|
222
|
+
0 0 6px hsl(var(--agent-realtime-voice-glow-color) / 40%),
|
|
223
|
+
0 0 15px hsl(var(--agent-realtime-voice-glow-color) / 30%),
|
|
224
|
+
0 0 25px 2px hsl(var(--agent-realtime-voice-glow-color) / 20%),
|
|
225
|
+
0 0 50px 2px hsl(var(--agent-realtime-voice-glow-color) / 10%);
|
|
79
226
|
}
|
|
80
227
|
|
|
81
228
|
@media (prefers-reduced-motion: reduce) {
|
|
82
229
|
.agent-realtime-voice-glow {
|
|
83
|
-
|
|
230
|
+
--agent-realtime-voice-angle: 35deg;
|
|
84
231
|
}
|
|
85
232
|
}
|
package/src/ui/button.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
LegacyButtonRenderContext,
|
|
7
|
-
|
|
7
|
+
useDesignSystem,
|
|
8
8
|
} from "../design-system/context.js";
|
|
9
9
|
import { DesignSystemErrorBoundary } from "../design-system/error-boundary.js";
|
|
10
10
|
import type {
|
|
@@ -83,7 +83,8 @@ const ButtonOverrideRenderContext = React.createContext(false);
|
|
|
83
83
|
|
|
84
84
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
85
85
|
({ variant, size, asChild = false, intent, emphasis, ...props }, ref) => {
|
|
86
|
-
const DesignSystemActionButton =
|
|
86
|
+
const DesignSystemActionButton =
|
|
87
|
+
useDesignSystem()?.components?.ActionButton;
|
|
87
88
|
const Override = useToolkitComponent("Button");
|
|
88
89
|
const isRenderingOverride = React.useContext(ButtonOverrideRenderContext);
|
|
89
90
|
const isRenderingLegacyButton = React.useContext(LegacyButtonRenderContext);
|