@simmalugnt-se/payload-editor-assistant 0.3.1 → 0.3.3
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 +13 -0
- package/README.md +9 -1
- package/dist/components/EditorAssistantAction.d.ts +1 -0
- package/dist/components/EditorAssistantAction.js +13 -7
- package/dist/components/EditorAssistantAside.d.ts +1 -1
- package/dist/components/EditorAssistantAside.js +5 -4
- package/dist/components/EditorAssistantProvider.js +2 -2
- package/dist/components/assistant-context.d.ts +2 -3
- package/dist/components/assistant-context.js +30 -6
- package/dist/components/copy.d.ts +2 -0
- package/dist/components/copy.js +2 -0
- package/dist/runtime/agent.js +2 -2
- package/dist/styles/assistant.css +37 -32
- package/dist/types.d.ts +5 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.3
|
|
4
|
+
|
|
5
|
+
- A `model` function may return `null` or `undefined`, for example when no API key is set. The drawer
|
|
6
|
+
then says that no AI model is set up instead of that the AI service did not answer, and the server
|
|
7
|
+
logs a warning instead of an error. Hosts no longer need a cast to keep the plugin installed
|
|
8
|
+
without a model.
|
|
9
|
+
|
|
10
|
+
## 0.3.2
|
|
11
|
+
|
|
12
|
+
- One control opens and closes the assistant: the text button with a sparkles icon in Admin's
|
|
13
|
+
header. The narrow rail shown while the panel was collapsed is gone.
|
|
14
|
+
- The panel starts closed, and the browser remembers whether you left it open.
|
|
15
|
+
|
|
3
16
|
## 0.3.1
|
|
4
17
|
|
|
5
18
|
- A greeting or a question no longer fills empty fields: the assistant fills them only when asked.
|
package/README.md
CHANGED
|
@@ -57,9 +57,17 @@ with its key, e.g. `createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY })`.
|
|
|
57
57
|
Providers also read a default environment variable (`DEEPSEEK_API_KEY`, `ANTHROPIC_API_KEY`, ...)
|
|
58
58
|
when no `apiKey` is passed, but passing it explicitly keeps the connection visible in your config.
|
|
59
59
|
|
|
60
|
-
If the key is
|
|
60
|
+
If the key is wrong, the drawer says the AI service did not answer and the cause is logged on the
|
|
61
61
|
server through Payload's logger.
|
|
62
62
|
|
|
63
|
+
To keep the plugin installed without a key (its tables stay in your schema and migrations), let
|
|
64
|
+
`model` be a function that returns `null` or `undefined` when there is nothing to call. The drawer then
|
|
65
|
+
tells the editor that no AI model is set up, and a warning is logged:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
model: () => (process.env.DEEPSEEK_API_KEY ? deepSeek("deepseek-chat") : null),
|
|
69
|
+
```
|
|
70
|
+
|
|
63
71
|
## What the assistant may do
|
|
64
72
|
|
|
65
73
|
Nothing is reachable unless you list it. Each collection or global gets the capabilities it allows:
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useAuth, useTranslation } from "@payloadcms/ui";
|
|
4
4
|
import { useAssistantState } from "./assistant-context.js";
|
|
5
|
+
import { COPY, uiLanguage } from "./copy.js";
|
|
6
|
+
/** The one control that opens and closes the assistant panel, in Admin's header. */
|
|
5
7
|
export function EditorAssistantAction() {
|
|
6
8
|
const { user } = useAuth();
|
|
7
|
-
const {
|
|
9
|
+
const { i18n } = useTranslation();
|
|
10
|
+
const { open, setOpen } = useAssistantState();
|
|
8
11
|
if (!user) {
|
|
9
12
|
return null;
|
|
10
13
|
}
|
|
11
|
-
return (
|
|
14
|
+
return (_jsxs("button", { "aria-expanded": open, className: open ? "ea-trigger ea-trigger--open" : "ea-trigger", onClick: (event) => {
|
|
12
15
|
event.preventDefault();
|
|
13
16
|
event.stopPropagation();
|
|
14
|
-
setOpen(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
setOpen(!open);
|
|
18
|
+
}, type: "button", children: [_jsx(SparklesIcon, {}), COPY[uiLanguage(i18n.language)].assistant] }));
|
|
19
|
+
}
|
|
20
|
+
/** A large four-point star with two small ones: the usual sign for AI help. */
|
|
21
|
+
function SparklesIcon() {
|
|
22
|
+
return (_jsxs("svg", { "aria-hidden": "true", className: "ea-trigger__icon", fill: "currentColor", viewBox: "0 0 20 20", children: [_jsx("path", { d: "M8 2.5l1.3 3.7a4 4 0 0 0 2.5 2.5L15.5 10l-3.7 1.3a4 4 0 0 0-2.5 2.5L8 17.5l-1.3-3.7a4 4 0 0 0-2.5-2.5L.5 10l3.7-1.3a4 4 0 0 0 2.5-2.5L8 2.5z" }), _jsx("path", { d: "M15.5 1l.6 1.6a1.5 1.5 0 0 0 .9.9l1.6.6-1.6.6a1.5 1.5 0 0 0-.9.9l-.6 1.6-.6-1.6a1.5 1.5 0 0 0-.9-.9L12.4 4.1l1.6-.6a1.5 1.5 0 0 0 .9-.9L15.5 1z" }), _jsx("path", { d: "M16 13.5l.4 1.1a1 1 0 0 0 .6.6l1.1.4-1.1.4a1 1 0 0 0-.6.6L16 17.7l-.4-1.1a1 1 0 0 0-.6-.6l-1.1-.4 1.1-.4a1 1 0 0 0 .6-.6l.4-1.1z" })] }));
|
|
17
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function EditorAssistantAside(): import("react").JSX.Element;
|
|
1
|
+
export declare function EditorAssistantAside(): import("react").JSX.Element | null;
|
|
@@ -9,7 +9,7 @@ import { useAssistantState, } from "./assistant-context.js";
|
|
|
9
9
|
import { COPY, uiLanguage } from "./copy.js";
|
|
10
10
|
import { KEEP_SELECTION_ATTR } from "./selection-dom.js";
|
|
11
11
|
export function EditorAssistantAside() {
|
|
12
|
-
const {
|
|
12
|
+
const { open, setOpen, status, messages, shortcuts, collections, bridge, formAdapter, pendingApproval, selection, setSelection, resetConversation, setStatus, setMessages, setPendingApproval, stop, setAbortController, } = useAssistantState();
|
|
13
13
|
const { config, getEntityConfig } = useConfig();
|
|
14
14
|
const router = useRouter();
|
|
15
15
|
const locale = useLocale();
|
|
@@ -334,10 +334,11 @@ export function EditorAssistantAside() {
|
|
|
334
334
|
void send(draft);
|
|
335
335
|
}
|
|
336
336
|
};
|
|
337
|
-
|
|
338
|
-
|
|
337
|
+
// Closed: nothing is shown. The component stays mounted, so an applied preview survives.
|
|
338
|
+
if (!open) {
|
|
339
|
+
return null;
|
|
339
340
|
}
|
|
340
|
-
return (_jsxs("aside", { className: "ea-aside", "aria-label": copy.assistant, [KEEP_SELECTION_ATTR]: "", children: [_jsxs("div", { className: "ea-aside__head", children: [_jsxs("div", { className: "ea-aside__title", children: [_jsx("h2", { children: copy.assistant }), bridge ? _jsx("p", { className: "ea-aside__context", children: contextLine(bridge, config) }) : null] }), (messages.length > 0 || pendingApproval) && !pendingApproval?.applied ? (_jsx(Button, { buttonStyle: "subtle", margin: false, onClick: () => resetConversation(), size: "medium", children: copy.newChat })) : null, _jsx(Button, { "aria-label": copy.collapseLabel, buttonStyle: "icon-label", className: "ea-aside__collapse", icon: _jsx(ChevronIcon, { direction: "right" }), margin: false, onClick: () =>
|
|
341
|
+
return (_jsxs("aside", { className: "ea-aside", "aria-label": copy.assistant, [KEEP_SELECTION_ATTR]: "", children: [_jsxs("div", { className: "ea-aside__head", children: [_jsxs("div", { className: "ea-aside__title", children: [_jsx("h2", { children: copy.assistant }), bridge ? _jsx("p", { className: "ea-aside__context", children: contextLine(bridge, config) }) : null] }), (messages.length > 0 || pendingApproval) && !pendingApproval?.applied ? (_jsx(Button, { buttonStyle: "subtle", margin: false, onClick: () => resetConversation(), size: "medium", children: copy.newChat })) : null, _jsx(Button, { "aria-label": copy.collapseLabel, buttonStyle: "icon-label", className: "ea-aside__collapse", icon: _jsx(ChevronIcon, { direction: "right" }), margin: false, onClick: () => setOpen(false), tooltip: copy.collapse })] }), _jsxs("div", { className: "ea-aside__body", children: [_jsxs("div", { className: "ea-messages", ref: threadRef, onScroll: onThreadScroll, children: [messages.length === 0 ? (_jsx("div", { className: "ea-msg ea-msg--assistant", children: introText(view, bridge, collections, config, copy) })) : null, messages.map((message) => (_jsxs("div", { className: `ea-msg ea-msg--${message.role}`, children: [message.text, message.candidates?.length ? (_jsx("div", { className: "ea-actions", children: message.candidates.map((candidate) => (_jsx(Button, { buttonStyle: "pill", margin: false, onClick: () => {
|
|
341
342
|
router.push(candidate.href);
|
|
342
343
|
}, size: "small", children: candidate.label }, candidate.href))) })) : null] }, message.id))), status === "streaming" ? (_jsxs("div", { className: "ea-msg ea-msg--working", children: [_jsxs("span", { className: "ea-dots", "aria-hidden": "true", children: [_jsx("span", {}), _jsx("span", {}), _jsx("span", {})] }), _jsx("span", { children: copy.working })] })) : null] }), pendingApproval ? (_jsxs("div", { className: "ea-approval", children: [_jsx("h3", { children: pendingApproval.title }), pendingApproval.applied ? (_jsx("p", { className: "ea-approval__hint", children: autosave ? copy.previewHintAutosave : copy.previewHint })) : null, _jsx("ul", { className: "ea-approval__diff", children: pendingApproval.summary.split("\n").map((line, index) => line.startsWith(" ") ? (
|
|
343
344
|
// biome-ignore lint/suspicious/noArrayIndexKey: lines are static for one approval
|
|
@@ -10,6 +10,6 @@ export function EditorAssistantProvider({ children, shortcuts = [], collections
|
|
|
10
10
|
return (_jsx(AssistantStateProvider, { shortcuts: shortcuts, collections: collections, children: user ? _jsx(EditorAssistantShell, { children: children }) : children }));
|
|
11
11
|
}
|
|
12
12
|
function EditorAssistantShell({ children }) {
|
|
13
|
-
const {
|
|
14
|
-
return (_jsxs("div", { className:
|
|
13
|
+
const { open } = useAssistantState();
|
|
14
|
+
return (_jsxs("div", { className: open ? "ea-root" : "ea-root ea-root--closed", children: [_jsx("div", { className: "ea-main", children: children }), _jsx(EditorAssistantAside, {}), _jsx(ChangedFieldsHighlight, {})] }));
|
|
15
15
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { AssistantShortcut } from "../types.ts";
|
|
3
3
|
import type { DomSelection } from "./selection-dom.ts";
|
|
4
|
-
export type AssistantChromeStatus = "idle" | "unavailable" | "unauthorized" | "rate_limited" | "timeout" | "provider_error" | "tool_denied" | "stale_context" | "aborted" | "streaming";
|
|
4
|
+
export type AssistantChromeStatus = "idle" | "unavailable" | "unauthorized" | "rate_limited" | "timeout" | "provider_error" | "no_model" | "tool_denied" | "stale_context" | "aborted" | "streaming";
|
|
5
5
|
export type DocumentBridgeSnapshot = {
|
|
6
6
|
collectionSlug?: string;
|
|
7
7
|
globalSlug?: string;
|
|
@@ -46,8 +46,8 @@ export type AssistantMessage = {
|
|
|
46
46
|
}>;
|
|
47
47
|
};
|
|
48
48
|
type AssistantContextValue = {
|
|
49
|
+
/** Whether the panel is shown. Closed at first; the browser remembers the choice. */
|
|
49
50
|
open: boolean;
|
|
50
|
-
collapsed: boolean;
|
|
51
51
|
status: AssistantChromeStatus;
|
|
52
52
|
messages: AssistantMessage[];
|
|
53
53
|
shortcuts: AssistantShortcut[];
|
|
@@ -59,7 +59,6 @@ type AssistantContextValue = {
|
|
|
59
59
|
/** What the editor last clicked or focused in the open form. */
|
|
60
60
|
selection: DomSelection | null;
|
|
61
61
|
setOpen: (open: boolean) => void;
|
|
62
|
-
setCollapsed: (collapsed: boolean) => void;
|
|
63
62
|
setBridge: (bridge: DocumentBridgeSnapshot | null) => void;
|
|
64
63
|
setFormAdapter: (adapter: FormAdapter | null) => void;
|
|
65
64
|
setPendingApproval: (pending: PendingApproval | null) => void;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react";
|
|
4
4
|
const AssistantContext = createContext(null);
|
|
5
5
|
export function AssistantStateProvider({ children, shortcuts, collections, }) {
|
|
6
|
-
const [open,
|
|
7
|
-
const [collapsed, setCollapsed] = useState(false);
|
|
6
|
+
const [open, setOpenState] = useState(false);
|
|
8
7
|
const [status, setStatus] = useState("idle");
|
|
9
8
|
const [messages, setMessages] = useState([]);
|
|
10
9
|
const [bridge, setBridge] = useState(null);
|
|
@@ -12,6 +11,15 @@ export function AssistantStateProvider({ children, shortcuts, collections, }) {
|
|
|
12
11
|
const [pendingApproval, setPendingApproval] = useState(null);
|
|
13
12
|
const [abortController, setAbortController] = useState(null);
|
|
14
13
|
const [selection, setSelection] = useState(null);
|
|
14
|
+
// Read after mount, so the server render (closed) and the first client render agree.
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (readStoredOpen())
|
|
17
|
+
setOpenState(true);
|
|
18
|
+
}, []);
|
|
19
|
+
const setOpen = useCallback((next) => {
|
|
20
|
+
setOpenState(next);
|
|
21
|
+
storeOpen(next);
|
|
22
|
+
}, []);
|
|
15
23
|
const stop = useCallback(() => {
|
|
16
24
|
abortController?.abort();
|
|
17
25
|
setAbortController(null);
|
|
@@ -26,7 +34,6 @@ export function AssistantStateProvider({ children, shortcuts, collections, }) {
|
|
|
26
34
|
}, [abortController]);
|
|
27
35
|
const value = useMemo(() => ({
|
|
28
36
|
open,
|
|
29
|
-
collapsed,
|
|
30
37
|
status,
|
|
31
38
|
messages,
|
|
32
39
|
shortcuts,
|
|
@@ -36,7 +43,6 @@ export function AssistantStateProvider({ children, shortcuts, collections, }) {
|
|
|
36
43
|
pendingApproval,
|
|
37
44
|
selection,
|
|
38
45
|
setOpen,
|
|
39
|
-
setCollapsed,
|
|
40
46
|
setBridge,
|
|
41
47
|
setFormAdapter,
|
|
42
48
|
setPendingApproval,
|
|
@@ -49,7 +55,6 @@ export function AssistantStateProvider({ children, shortcuts, collections, }) {
|
|
|
49
55
|
setAbortController,
|
|
50
56
|
}), [
|
|
51
57
|
open,
|
|
52
|
-
collapsed,
|
|
53
58
|
status,
|
|
54
59
|
messages,
|
|
55
60
|
shortcuts,
|
|
@@ -58,6 +63,7 @@ export function AssistantStateProvider({ children, shortcuts, collections, }) {
|
|
|
58
63
|
formAdapter,
|
|
59
64
|
pendingApproval,
|
|
60
65
|
selection,
|
|
66
|
+
setOpen,
|
|
61
67
|
resetConversation,
|
|
62
68
|
stop,
|
|
63
69
|
abortController,
|
|
@@ -71,3 +77,21 @@ export function useAssistantState() {
|
|
|
71
77
|
}
|
|
72
78
|
return value;
|
|
73
79
|
}
|
|
80
|
+
const OPEN_KEY = "payload-editor-assistant:open";
|
|
81
|
+
/** Storage can be unavailable (private windows, blocked site data); the panel then starts closed. */
|
|
82
|
+
function readStoredOpen() {
|
|
83
|
+
try {
|
|
84
|
+
return window.localStorage.getItem(OPEN_KEY) === "true";
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function storeOpen(open) {
|
|
91
|
+
try {
|
|
92
|
+
window.localStorage.setItem(OPEN_KEY, String(open));
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Not remembered; nothing else depends on it.
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -43,6 +43,7 @@ export declare const COPY: {
|
|
|
43
43
|
readonly rate_limited: "Too many messages at once. Wait a moment and try again.";
|
|
44
44
|
readonly timeout: "The assistant took too long to answer. Try again.";
|
|
45
45
|
readonly provider_error: "The AI service did not answer. The server log has the cause.";
|
|
46
|
+
readonly no_model: "No AI model is set up yet, for example because an API key is missing. Ask whoever runs the site to add one.";
|
|
46
47
|
readonly tool_denied: "That is not something the assistant may do here.";
|
|
47
48
|
readonly stale_context: "The form changed in the meantime. Try again.";
|
|
48
49
|
readonly aborted: "Stopped.";
|
|
@@ -93,6 +94,7 @@ export declare const COPY: {
|
|
|
93
94
|
readonly rate_limited: "För många meddelanden på en gång. Vänta en stund och försök igen.";
|
|
94
95
|
readonly timeout: "Assistenten tog för lång tid på sig. Försök igen.";
|
|
95
96
|
readonly provider_error: "AI-tjänsten svarade inte. Orsaken finns i serverloggen.";
|
|
97
|
+
readonly no_model: "Ingen AI-modell är inställd än, till exempel för att en API-nyckel saknas. Be den som sköter webbplatsen att lägga till en.";
|
|
96
98
|
readonly tool_denied: "Det får assistenten inte göra här.";
|
|
97
99
|
readonly stale_context: "Formuläret ändrades under tiden. Försök igen.";
|
|
98
100
|
readonly aborted: "Stoppat.";
|
package/dist/components/copy.js
CHANGED
|
@@ -44,6 +44,7 @@ export const COPY = {
|
|
|
44
44
|
rate_limited: "Too many messages at once. Wait a moment and try again.",
|
|
45
45
|
timeout: "The assistant took too long to answer. Try again.",
|
|
46
46
|
provider_error: "The AI service did not answer. The server log has the cause.",
|
|
47
|
+
no_model: "No AI model is set up yet, for example because an API key is missing. Ask whoever runs the site to add one.",
|
|
47
48
|
tool_denied: "That is not something the assistant may do here.",
|
|
48
49
|
stale_context: "The form changed in the meantime. Try again.",
|
|
49
50
|
aborted: "Stopped.",
|
|
@@ -94,6 +95,7 @@ export const COPY = {
|
|
|
94
95
|
rate_limited: "För många meddelanden på en gång. Vänta en stund och försök igen.",
|
|
95
96
|
timeout: "Assistenten tog för lång tid på sig. Försök igen.",
|
|
96
97
|
provider_error: "AI-tjänsten svarade inte. Orsaken finns i serverloggen.",
|
|
98
|
+
no_model: "Ingen AI-modell är inställd än, till exempel för att en API-nyckel saknas. Be den som sköter webbplatsen att lägga till en.",
|
|
97
99
|
tool_denied: "Det får assistenten inte göra här.",
|
|
98
100
|
stale_context: "Formuläret ändrades under tiden. Försök igen.",
|
|
99
101
|
aborted: "Stoppat.",
|
package/dist/runtime/agent.js
CHANGED
|
@@ -10,8 +10,8 @@ const TIMEOUT_MS = 60_000;
|
|
|
10
10
|
export async function runAgentTurn(req, options, input) {
|
|
11
11
|
const model = await resolveModel(options, req.user?.id);
|
|
12
12
|
if (!model) {
|
|
13
|
-
|
|
14
|
-
return { ok: false, error: "
|
|
13
|
+
req.payload?.logger?.warn(`${PACKAGE_NAME}: no model: the \`model\` option resolved to nothing for this user`);
|
|
14
|
+
return { ok: false, error: "no_model" };
|
|
15
15
|
}
|
|
16
16
|
const ai = await loadAiSdk();
|
|
17
17
|
if (!ai) {
|
|
@@ -8,10 +8,6 @@
|
|
|
8
8
|
overflow: hidden;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
.ea-root--collapsed {
|
|
12
|
-
--ea-aside-width: 48px;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
11
|
html:has(.ea-root),
|
|
16
12
|
html:has(.ea-root) body {
|
|
17
13
|
height: 100%;
|
|
@@ -54,34 +50,6 @@ html[data-theme="dark"] .ea-aside {
|
|
|
54
50
|
--ea-accent-tint: rgba(96, 165, 250, 0.14);
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
.ea-aside--collapsed {
|
|
58
|
-
border-inline-start-color: var(--theme-elevation-100);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.ea-aside__rail {
|
|
62
|
-
display: flex;
|
|
63
|
-
flex-direction: column;
|
|
64
|
-
align-items: center;
|
|
65
|
-
gap: calc(var(--base) * 0.5);
|
|
66
|
-
width: 100%;
|
|
67
|
-
height: 100%;
|
|
68
|
-
padding: calc(var(--base) * 0.8) 0;
|
|
69
|
-
border: 0;
|
|
70
|
-
background: transparent;
|
|
71
|
-
color: var(--theme-elevation-500);
|
|
72
|
-
font: inherit;
|
|
73
|
-
cursor: pointer;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.ea-aside__rail span {
|
|
77
|
-
writing-mode: vertical-rl;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.ea-aside__rail:hover {
|
|
81
|
-
color: var(--theme-text);
|
|
82
|
-
background: var(--theme-elevation-50);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
53
|
.ea-aside__head {
|
|
86
54
|
display: flex;
|
|
87
55
|
align-items: center;
|
|
@@ -386,8 +354,41 @@ html[data-theme="dark"] .ea-aside {
|
|
|
386
354
|
gap: calc(var(--base) * 0.4);
|
|
387
355
|
}
|
|
388
356
|
|
|
357
|
+
/* Header control: text with an icon, like Admin's other header links rather than a button. */
|
|
389
358
|
.ea-trigger {
|
|
359
|
+
display: inline-flex;
|
|
360
|
+
align-items: center;
|
|
361
|
+
gap: 6px;
|
|
390
362
|
margin-inline: 8px;
|
|
363
|
+
padding: 6px 4px;
|
|
364
|
+
border: 0;
|
|
365
|
+
border-radius: var(--style-radius-s, 3px);
|
|
366
|
+
background: none;
|
|
367
|
+
color: var(--theme-elevation-800);
|
|
368
|
+
font: inherit;
|
|
369
|
+
font-size: 14px;
|
|
370
|
+
line-height: 1;
|
|
371
|
+
cursor: pointer;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.ea-trigger:hover,
|
|
375
|
+
.ea-trigger--open {
|
|
376
|
+
color: var(--theme-text);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.ea-trigger:focus-visible {
|
|
380
|
+
outline: 2px solid var(--ea-accent, #2563eb);
|
|
381
|
+
outline-offset: 2px;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.ea-trigger__icon {
|
|
385
|
+
width: 16px;
|
|
386
|
+
height: 16px;
|
|
387
|
+
flex-shrink: 0;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.ea-trigger--open .ea-trigger__icon {
|
|
391
|
+
color: var(--ea-accent, #2563eb);
|
|
391
392
|
}
|
|
392
393
|
|
|
393
394
|
.ea-stop-icon {
|
|
@@ -397,3 +398,7 @@ html[data-theme="dark"] .ea-aside {
|
|
|
397
398
|
border-radius: 2px;
|
|
398
399
|
background: currentColor;
|
|
399
400
|
}
|
|
401
|
+
|
|
402
|
+
html[data-theme="dark"] .ea-trigger--open .ea-trigger__icon {
|
|
403
|
+
color: #60a5fa;
|
|
404
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { EntityCapability } from "./capabilities.ts";
|
|
2
2
|
import type { AdminViewKind } from "./view.ts";
|
|
3
3
|
export type HostLanguageModel = object;
|
|
4
|
+
/**
|
|
5
|
+
* Returns the model for this request. Returning `null` or `undefined` (no API key set, say) keeps the
|
|
6
|
+
* plugin installed and tells the editor in the drawer that no model is set up.
|
|
7
|
+
*/
|
|
4
8
|
export type ModelResolver = (args: {
|
|
5
9
|
userId?: string | number;
|
|
6
|
-
}) => HostLanguageModel | Promise<HostLanguageModel>;
|
|
10
|
+
}) => HostLanguageModel | null | undefined | Promise<HostLanguageModel | null | undefined>;
|
|
7
11
|
export type FieldAllowlist = true | {
|
|
8
12
|
include: string[];
|
|
9
13
|
} | {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simmalugnt-se/payload-editor-assistant",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Context-aware editor assistant plugin for Payload Admin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"payload",
|
|
@@ -53,12 +53,12 @@
|
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@payloadcms/ui": "3.
|
|
56
|
+
"@payloadcms/ui": "3.90.2",
|
|
57
57
|
"@types/node": "^22",
|
|
58
58
|
"@types/react": "^19.2.18",
|
|
59
59
|
"@types/react-dom": "^19.2.4",
|
|
60
|
-
"next": "16.3.
|
|
61
|
-
"payload": "3.
|
|
60
|
+
"next": "16.3.6",
|
|
61
|
+
"payload": "3.90.2",
|
|
62
62
|
"react": "19.2.8",
|
|
63
63
|
"react-dom": "19.2.8",
|
|
64
64
|
"tsx": "^4.21.0",
|