pai-agent-ui 0.1.0

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.
Files changed (63) hide show
  1. package/README.md +57 -0
  2. package/dist/components/AgentChatPanel.d.ts +8 -0
  3. package/dist/components/AgentChatPanel.d.ts.map +1 -0
  4. package/dist/components/AgentChatPanel.js +88 -0
  5. package/dist/components/AgentChatPanel.js.map +1 -0
  6. package/dist/components/AgentWidgetRenderer.d.ts +3 -0
  7. package/dist/components/AgentWidgetRenderer.d.ts.map +1 -0
  8. package/dist/components/AgentWidgetRenderer.js +40 -0
  9. package/dist/components/AgentWidgetRenderer.js.map +1 -0
  10. package/dist/components/ChatComposer.d.ts +9 -0
  11. package/dist/components/ChatComposer.d.ts.map +1 -0
  12. package/dist/components/ChatComposer.js +138 -0
  13. package/dist/components/ChatComposer.js.map +1 -0
  14. package/dist/components/EntityTreeEditor.d.ts +22 -0
  15. package/dist/components/EntityTreeEditor.d.ts.map +1 -0
  16. package/dist/components/EntityTreeEditor.js +178 -0
  17. package/dist/components/EntityTreeEditor.js.map +1 -0
  18. package/dist/components/JobStatusLine.d.ts +13 -0
  19. package/dist/components/JobStatusLine.d.ts.map +1 -0
  20. package/dist/components/JobStatusLine.js +32 -0
  21. package/dist/components/JobStatusLine.js.map +1 -0
  22. package/dist/components/widgets/AgentPlanWidget.d.ts +6 -0
  23. package/dist/components/widgets/AgentPlanWidget.d.ts.map +1 -0
  24. package/dist/components/widgets/AgentPlanWidget.js +42 -0
  25. package/dist/components/widgets/AgentPlanWidget.js.map +1 -0
  26. package/dist/components/widgets/ClarificationRequestWidget.d.ts +6 -0
  27. package/dist/components/widgets/ClarificationRequestWidget.d.ts.map +1 -0
  28. package/dist/components/widgets/ClarificationRequestWidget.js +49 -0
  29. package/dist/components/widgets/ClarificationRequestWidget.js.map +1 -0
  30. package/dist/components/widgets/ExecutionActionWidget.d.ts +6 -0
  31. package/dist/components/widgets/ExecutionActionWidget.d.ts.map +1 -0
  32. package/dist/components/widgets/ExecutionActionWidget.js +320 -0
  33. package/dist/components/widgets/ExecutionActionWidget.js.map +1 -0
  34. package/dist/components/widgets/PaceFileViewWidget.d.ts +6 -0
  35. package/dist/components/widgets/PaceFileViewWidget.d.ts.map +1 -0
  36. package/dist/components/widgets/PaceFileViewWidget.js +78 -0
  37. package/dist/components/widgets/PaceFileViewWidget.js.map +1 -0
  38. package/dist/components/widgets/PacePatchFormWidget.d.ts +6 -0
  39. package/dist/components/widgets/PacePatchFormWidget.d.ts.map +1 -0
  40. package/dist/components/widgets/PacePatchFormWidget.js +112 -0
  41. package/dist/components/widgets/PacePatchFormWidget.js.map +1 -0
  42. package/dist/components/widgets/ProjectMemoryReviewWidget.d.ts +6 -0
  43. package/dist/components/widgets/ProjectMemoryReviewWidget.d.ts.map +1 -0
  44. package/dist/components/widgets/ProjectMemoryReviewWidget.js +50 -0
  45. package/dist/components/widgets/ProjectMemoryReviewWidget.js.map +1 -0
  46. package/dist/index.d.ts +7 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +7 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/styles.css +643 -0
  51. package/dist/types.d.ts +317 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +2 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/utils/jsonPointer.d.ts +20 -0
  56. package/dist/utils/jsonPointer.d.ts.map +1 -0
  57. package/dist/utils/jsonPointer.js +220 -0
  58. package/dist/utils/jsonPointer.js.map +1 -0
  59. package/dist/utils/workerTask.d.ts +30 -0
  60. package/dist/utils/workerTask.d.ts.map +1 -0
  61. package/dist/utils/workerTask.js +90 -0
  62. package/dist/utils/workerTask.js.map +1 -0
  63. package/package.json +40 -0
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # PAI Agent UI
2
+
3
+ Reusable React components for rendering PAI Agent chat widgets.
4
+
5
+ The package is UI-only. It does not own authentication, GraphQL clients, routing, query cache, or toast systems. Consumers provide those through `AgentWidgetAdapter`.
6
+
7
+ ## Exports
8
+
9
+ * `AgentChatPanel`: full chat panel with message area, active AgentRun display, widgets, and composer.
10
+ * `ChatComposer`: chat input with `@` entity completion.
11
+ * `AgentWidgetRenderer`: renders Agent widget payloads by widget type.
12
+ * `EntityTreeEditor`: tree editor for PACE JSON files and patch previews.
13
+ * `JobStatusLine`: worker job status/progress display.
14
+
15
+ ## Adapter Boundary
16
+
17
+ ```ts
18
+ import { AgentChatPanel, type AgentWidgetAdapter } from 'pai-agent-ui';
19
+
20
+ const adapter: AgentWidgetAdapter = {
21
+ selectedProjectId,
22
+ fieldTerms,
23
+ getWorkers,
24
+ submitExecution,
25
+ submitAgentRunWidget,
26
+ patchPaceFiles,
27
+ getJob,
28
+ onDataChanged,
29
+ notify,
30
+ };
31
+ ```
32
+
33
+ `fieldTerms` should come from the backend PACE standard schema, not from a frontend-maintained field table. The built-in frontend derives it from `paceStandardSchema(name)`.
34
+
35
+ ```tsx
36
+ <AgentChatPanel
37
+ agentMode="agent-v2"
38
+ messages={messages}
39
+ runs={agentRuns}
40
+ isWaiting={isWaiting}
41
+ widgetAdapter={adapter}
42
+ suggestions={entitySuggestions}
43
+ composerDisabled={!canSend}
44
+ composerSending={sending}
45
+ onSend={sendMessage}
46
+ />
47
+ ```
48
+
49
+ The official frontend should map these callbacks to its own GraphQL client and cache invalidation. Do not put app-specific stores or session handling inside this package.
50
+
51
+ ## Styles
52
+
53
+ Import the package stylesheet once:
54
+
55
+ ```ts
56
+ import 'pai-agent-ui/styles.css';
57
+ ```
@@ -0,0 +1,8 @@
1
+ import type { AgentChatPanelProps, ChatMessageLike } from "../types";
2
+ export declare function AgentChatPanel({ messages, runs, agentMode, isWaiting, widgetAdapter, suggestions, composerDisabled, composerSending, composerPlaceholder, onSend, showRunDebug, }: AgentChatPanelProps): import("react").JSX.Element;
3
+ export declare function AgentMessageArea({ messages, runs, agentMode, isWaiting, widgetAdapter, showRunDebug, }: Pick<AgentChatPanelProps, "messages" | "runs" | "agentMode" | "isWaiting" | "widgetAdapter" | "showRunDebug">): import("react").JSX.Element;
4
+ export declare function AssistantMessage({ message, widgetAdapter, }: {
5
+ message: ChatMessageLike;
6
+ widgetAdapter: AgentChatPanelProps["widgetAdapter"];
7
+ }): import("react").JSX.Element;
8
+ //# sourceMappingURL=AgentChatPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentChatPanel.d.ts","sourceRoot":"","sources":["../../src/components/AgentChatPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAgB,eAAe,EAAE,MAAM,UAAU,CAAC;AAOnF,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,IAAS,EACT,SAAsB,EACtB,SAAS,EACT,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,MAAM,EACN,YAAoB,GACrB,EAAE,mBAAmB,+BAoBrB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,IAAS,EACT,SAAsB,EACtB,SAAS,EACT,aAAa,EACb,YAAoB,GACrB,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,GAAG,eAAe,GAAG,cAAc,CAAC,+BAmC/G;AAiDD,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,aAAa,GACd,EAAE;IACD,OAAO,EAAE,eAAe,CAAC;IACzB,aAAa,EAAE,mBAAmB,CAAC,eAAe,CAAC,CAAC;CACrD,+BAqBA"}
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef, useState } from "react";
3
+ import { AgentWidgetRenderer } from "./AgentWidgetRenderer";
4
+ import { ChatComposer } from "./ChatComposer";
5
+ const ACTIVE_RUN_STATUSES = new Set(["needs_clarification", "waiting_for_confirmation", "waiting_for_next_step", "running_step", "failed"]);
6
+ const imageRegex = /(https?:\/\/[^\s)"']+\.(?:png|jpg|jpeg|webp)(?:\?[^\s)"']*)?)/gi;
7
+ export function AgentChatPanel({ messages, runs = [], agentMode = "agent-v2", isWaiting, widgetAdapter, suggestions, composerDisabled, composerSending, composerPlaceholder, onSend, showRunDebug = false, }) {
8
+ return (_jsxs("div", { className: "pai-agent-chat-panel", children: [_jsx(AgentMessageArea, { messages: messages, runs: runs, agentMode: agentMode, isWaiting: isWaiting, widgetAdapter: widgetAdapter, showRunDebug: showRunDebug }), _jsx(ChatComposer, { disabled: composerDisabled, sending: composerSending, suggestions: suggestions, placeholder: composerPlaceholder, onSend: onSend })] }));
9
+ }
10
+ export function AgentMessageArea({ messages, runs = [], agentMode = "agent-v2", isWaiting, widgetAdapter, showRunDebug = false, }) {
11
+ const ref = useRef(null);
12
+ const activeRuns = useMemo(() => {
13
+ if (agentMode !== "agent-v2") {
14
+ return [];
15
+ }
16
+ return runs.filter((run) => ACTIVE_RUN_STATUSES.has(run.status) || Object.keys(run.widgets || {}).length > 0);
17
+ }, [agentMode, runs]);
18
+ useEffect(() => {
19
+ if (ref.current) {
20
+ ref.current.scrollTop = ref.current.scrollHeight;
21
+ }
22
+ }, [messages, activeRuns, isWaiting]);
23
+ return (_jsxs("div", { className: "pai-messages-container", ref: ref, children: [messages.length === 0 && activeRuns.length === 0 ? (_jsx("div", { className: "pai-empty-state", children: "\u8FD8\u6CA1\u6709\u6D88\u606F\u3002\u53EF\u4EE5\u5148\u548C Agent \u804A\u804A\u955C\u5934\u3001\u5173\u952E\u5E27\u6216\u6E32\u67D3\u9700\u6C42\u3002" })) : null, messages.map((message) => (_jsx(MessageCard, { message: agentMode === "agent-v2" ? stripActionWidgets(message) : message, widgetAdapter: widgetAdapter }, message.id))), activeRuns.length ? _jsx("div", { className: "pai-agent-run-divider", children: "AgentRun \u72B6\u6001\u4E0E\u53EF\u63D0\u4EA4\u8868\u5355" }) : null, activeRuns.map((run) => (_jsx(AgentRunCard, { run: run, widgetAdapter: widgetAdapter, showDebug: showRunDebug }, run.runId))), isWaiting ? (_jsxs("article", { className: "pai-message-card assistant", children: [_jsx("div", { className: "pai-message-role", children: agentMode === "agent-v2" ? "agent v2" : "assistant" }), _jsx("div", { className: "pai-muted", children: agentMode === "agent-v2" ? "Agent v2 思考中..." : "Agent 思考中..." })] })) : null] }));
24
+ }
25
+ function MessageCard({ message, widgetAdapter }) {
26
+ return (_jsxs("article", { className: `pai-message-card ${message.role === "user" ? "user" : "assistant"}`, children: [_jsx("div", { className: "pai-message-role", children: message.role }), message.role === "assistant" && message.content_json ? (_jsx(AssistantMessage, { message: message, widgetAdapter: widgetAdapter })) : (_jsx("div", { className: "pai-message-content", children: message.content_text })), _jsx("div", { className: "pai-message-meta", children: new Date(message.created_at).toLocaleString() })] }));
27
+ }
28
+ function AgentRunCard({ run, widgetAdapter, showDebug, }) {
29
+ return (_jsxs("article", { className: "pai-message-card assistant", children: [_jsx("div", { className: "pai-message-role", children: "agent v2" }), _jsxs("div", { className: "pai-agent-run-header", children: [_jsx("span", { children: run.runId }), _jsx("span", { className: `pai-tag ${run.status === "failed" ? "is-failed" : run.status === "completed" ? "is-done" : ""}`, children: run.status })] }), run.projection ? (_jsxs("div", { className: "pai-agent-run-projection", children: [run.projection.awaitingKind ? _jsxs("span", { className: "pai-tag", children: ["\u7B49\u5F85\uFF1A", run.projection.awaitingKind] }) : null, run.projection.canResumeGraph ? _jsx("span", { className: "pai-tag", children: "graph checkpoint \u53EF\u6062\u590D" }) : _jsx("span", { className: "pai-tag", children: "graph idle" }), (run.projection.activeWidgetTypes || []).map((type) => (_jsx("span", { className: "pai-tag", children: type }, type)))] })) : null, _jsx(AssistantMessage, { message: runToMessage(run), widgetAdapter: widgetAdapter }), showDebug ? _jsx(RunDebug, { run: run }) : null] }));
30
+ }
31
+ export function AssistantMessage({ message, widgetAdapter, }) {
32
+ const payload = message.content_json || {};
33
+ return (_jsxs("div", { className: "pai-assistant-block", children: [_jsx(SummaryContent, { text: payload.summary || message.content_text || "" }), Array.isArray(payload.warnings) && payload.warnings.length ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "pai-subtle-label", children: "\u8B66\u544A" }), _jsx("ul", { className: "pai-warning-list", children: payload.warnings.map((warning) => (_jsx("li", { children: warning }, warning))) })] })) : null, _jsx(AgentWidgetRenderer, { widgets: payload.widgets, message: message, adapter: widgetAdapter }), payload.context_used && Object.keys(payload.context_used).length ? (_jsx(DetailsBlock, { title: "\u4F7F\u7528\u7684\u4E0A\u4E0B\u6587", value: payload.context_used })) : null] }));
34
+ }
35
+ function SummaryContent({ text }) {
36
+ const nodes = [];
37
+ let lastIndex = 0;
38
+ let match;
39
+ imageRegex.lastIndex = 0;
40
+ while ((match = imageRegex.exec(text)) !== null) {
41
+ if (match.index > lastIndex) {
42
+ nodes.push(text.slice(lastIndex, match.index));
43
+ }
44
+ nodes.push(_jsx("img", { className: "pai-storyboard-img", src: match[1], alt: "storyboard panel", loading: "lazy" }, `${match[1]}-${match.index}`));
45
+ lastIndex = imageRegex.lastIndex;
46
+ }
47
+ if (lastIndex < text.length) {
48
+ nodes.push(text.slice(lastIndex));
49
+ }
50
+ return _jsx("div", { className: "pai-assistant-summary", children: nodes.length ? nodes : text });
51
+ }
52
+ function RunDebug({ run }) {
53
+ return _jsx(DetailsBlock, { title: "Run \u72B6\u6001", value: { projection: run.projection, plan: run.plan, steps: run.steps, events: run.events } });
54
+ }
55
+ function DetailsBlock({ title, value }) {
56
+ const [open, setOpen] = useState(false);
57
+ return (_jsxs("div", { className: "pai-details", children: [_jsx("button", { type: "button", className: "pai-button pai-button-ghost", onClick: () => setOpen((current) => !current), children: open ? `收起${title}` : title }), open ? _jsx("pre", { className: "pai-pre", children: JSON.stringify(value, null, 2) }) : null] }));
58
+ }
59
+ function stripActionWidgets(message) {
60
+ if (!message.content_json?.widgets) {
61
+ return message;
62
+ }
63
+ return {
64
+ ...message,
65
+ content_json: {
66
+ ...message.content_json,
67
+ widgets: {},
68
+ run: undefined,
69
+ },
70
+ };
71
+ }
72
+ function runToMessage(run) {
73
+ return {
74
+ id: run.runId,
75
+ role: "assistant",
76
+ content_text: run.summary,
77
+ content_json: {
78
+ summary: run.summary,
79
+ warnings: run.warnings,
80
+ widgets: run.widgets,
81
+ plan: run.plan,
82
+ context_used: run.projection?.graph,
83
+ run,
84
+ },
85
+ created_at: new Date().toISOString(),
86
+ };
87
+ }
88
+ //# sourceMappingURL=AgentChatPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentChatPanel.js","sourceRoot":"","sources":["../../src/components/AgentChatPanel.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { AgentWidgetRendererProps } from "../types";
2
+ export declare function AgentWidgetRenderer({ widgets, message, adapter }: AgentWidgetRendererProps): import("react").JSX.Element | null;
3
+ //# sourceMappingURL=AgentWidgetRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentWidgetRenderer.d.ts","sourceRoot":"","sources":["../../src/components/AgentWidgetRenderer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,wBAAwB,EAMzB,MAAM,UAAU,CAAC;AAYlB,wBAAgB,mBAAmB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,wBAAwB,sCAU1F"}
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { AgentPlanWidget } from "./widgets/AgentPlanWidget";
3
+ import { ClarificationRequestWidget } from "./widgets/ClarificationRequestWidget";
4
+ import { ExecutionActionWidget } from "./widgets/ExecutionActionWidget";
5
+ import { PaceFileViewWidget } from "./widgets/PaceFileViewWidget";
6
+ import { PacePatchFormWidget } from "./widgets/PacePatchFormWidget";
7
+ import { ProjectMemoryReviewWidget } from "./widgets/ProjectMemoryReviewWidget";
8
+ function baseWidgetType(type) {
9
+ return type.split("__")[0];
10
+ }
11
+ export function AgentWidgetRenderer({ widgets, message, adapter }) {
12
+ if (!widgets || Object.keys(widgets).length === 0)
13
+ return null;
14
+ return (_jsx("div", { className: "pai-widget-stack", children: Object.entries(widgets).map(([type, data]) => {
15
+ const content = renderWidget(baseWidgetType(type), data, adapter, message);
16
+ return content ? _jsx("div", { children: content }, type) : null;
17
+ }) }));
18
+ }
19
+ function renderWidget(type, data, adapter, message) {
20
+ if (type === "pace_file_view") {
21
+ return _jsx(PaceFileViewWidget, { data: data, adapter: adapter });
22
+ }
23
+ if (type === "pace_patch_form") {
24
+ return _jsx(PacePatchFormWidget, { data: data, adapter: adapter });
25
+ }
26
+ if (type === "run_worker_task_form") {
27
+ return _jsx(ExecutionActionWidget, { data: data, adapter: adapter });
28
+ }
29
+ if (type === "clarification_request") {
30
+ return _jsx(ClarificationRequestWidget, { data: data, adapter: adapter });
31
+ }
32
+ if (type === "agent_plan") {
33
+ return _jsx(AgentPlanWidget, { data: data, adapter: adapter });
34
+ }
35
+ if (type === "project_memory_review_form") {
36
+ return _jsx(ProjectMemoryReviewWidget, { data: data, adapter: adapter });
37
+ }
38
+ return adapter.renderUnknownWidget?.({ type, data, message }) ?? null;
39
+ }
40
+ //# sourceMappingURL=AgentWidgetRenderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentWidgetRenderer.js","sourceRoot":"","sources":["../../src/components/AgentWidgetRenderer.tsx"],"names":[],"mappings":";AAUA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAClF,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAEhF,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAA4B;IACzF,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/D,OAAO,CACL,cAAK,SAAS,EAAC,kBAAkB,YAC9B,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;YAC5C,MAAM,OAAO,GAAG,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAC3E,OAAO,OAAO,CAAC,CAAC,CAAC,wBAAiB,OAAO,IAAd,IAAI,CAAiB,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1D,CAAC,CAAC,GACE,CACP,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,IAAa,EAAE,OAA4C,EAAE,OAA4C;IAC3I,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;QAC9B,OAAO,KAAC,kBAAkB,IAAC,IAAI,EAAE,IAAwB,EAAE,OAAO,EAAE,OAAO,GAAI,CAAC;IAClF,CAAC;IACD,IAAI,IAAI,KAAK,iBAAiB,EAAE,CAAC;QAC/B,OAAO,KAAC,mBAAmB,IAAC,IAAI,EAAE,IAAyB,EAAE,OAAO,EAAE,OAAO,GAAI,CAAC;IACpF,CAAC;IACD,IAAI,IAAI,KAAK,sBAAsB,EAAE,CAAC;QACpC,OAAO,KAAC,qBAAqB,IAAC,IAAI,EAAE,IAAgC,EAAE,OAAO,EAAE,OAAO,GAAI,CAAC;IAC7F,CAAC;IACD,IAAI,IAAI,KAAK,uBAAuB,EAAE,CAAC;QACrC,OAAO,KAAC,0BAA0B,IAAC,IAAI,EAAE,IAAgC,EAAE,OAAO,EAAE,OAAO,GAAI,CAAC;IAClG,CAAC;IACD,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,OAAO,KAAC,eAAe,IAAC,IAAI,EAAE,IAAqB,EAAE,OAAO,EAAE,OAAO,GAAI,CAAC;IAC5E,CAAC;IACD,IAAI,IAAI,KAAK,4BAA4B,EAAE,CAAC;QAC1C,OAAO,KAAC,yBAAyB,IAAC,IAAI,EAAE,IAA+B,EAAE,OAAO,EAAE,OAAO,GAAI,CAAC;IAChG,CAAC;IACD,OAAO,OAAO,CAAC,mBAAmB,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,IAAI,CAAC;AACxE,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { ChatEntitySuggestion } from "../types";
2
+ export declare function ChatComposer({ disabled, sending, placeholder, suggestions, onSend, }: {
3
+ disabled: boolean;
4
+ sending: boolean;
5
+ placeholder?: string;
6
+ suggestions: ChatEntitySuggestion[];
7
+ onSend: (message: string) => Promise<unknown> | unknown;
8
+ }): import("react").JSX.Element;
9
+ //# sourceMappingURL=ChatComposer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatComposer.d.ts","sourceRoot":"","sources":["../../src/components/ChatComposer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AA0CrD,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,OAAO,EACP,WAAW,EACX,WAAW,EACX,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CACzD,+BAkJA"}
@@ -0,0 +1,138 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef, useState } from "react";
3
+ const ENTITY_TYPE_LABELS = {
4
+ scene: "场景",
5
+ shot: "镜头",
6
+ panel: "关键帧",
7
+ character: "角色",
8
+ prop: "道具",
9
+ location: "地点",
10
+ voice: "声音",
11
+ };
12
+ function hasSuggestionId(suggestion) {
13
+ return typeof suggestion?.id === "string" && suggestion.id.trim().length > 0;
14
+ }
15
+ function findMentionMatch(message, cursor) {
16
+ if (cursor < 0 || cursor > message.length) {
17
+ return null;
18
+ }
19
+ const left = message.slice(0, cursor);
20
+ const atIndex = left.lastIndexOf("@");
21
+ if (atIndex < 0) {
22
+ return null;
23
+ }
24
+ const previousChar = atIndex === 0 ? "" : message[atIndex - 1];
25
+ if (previousChar && !/\s/.test(previousChar)) {
26
+ return null;
27
+ }
28
+ const query = message.slice(atIndex + 1, cursor);
29
+ if (/\s/.test(query)) {
30
+ return null;
31
+ }
32
+ return { start: atIndex, end: cursor, query };
33
+ }
34
+ export function ChatComposer({ disabled, sending, placeholder, suggestions, onSend, }) {
35
+ const [message, setMessage] = useState("");
36
+ const [cursor, setCursor] = useState(0);
37
+ const [activeIndex, setActiveIndex] = useState(0);
38
+ const inputRef = useRef(null);
39
+ async function submit() {
40
+ const value = message.trim();
41
+ if (!value || disabled || sending)
42
+ return;
43
+ await onSend(value);
44
+ setMessage("");
45
+ setCursor(0);
46
+ setActiveIndex(0);
47
+ }
48
+ const mentionMatch = useMemo(() => {
49
+ if (disabled) {
50
+ return null;
51
+ }
52
+ return findMentionMatch(message, cursor);
53
+ }, [cursor, disabled, message]);
54
+ const filteredSuggestions = useMemo(() => {
55
+ if (!mentionMatch) {
56
+ return [];
57
+ }
58
+ const query = mentionMatch.query.toLowerCase();
59
+ const validSuggestions = suggestions.filter(hasSuggestionId);
60
+ const rows = !query ? validSuggestions : validSuggestions.filter((suggestion) => suggestionMatchesQuery(suggestion, query));
61
+ return rows.slice(0, 10);
62
+ }, [mentionMatch, suggestions]);
63
+ const suggestionsOpen = Boolean(mentionMatch);
64
+ useEffect(() => {
65
+ setActiveIndex(0);
66
+ }, [mentionMatch?.start, mentionMatch?.query]);
67
+ function syncCursor() {
68
+ const textarea = inputRef.current;
69
+ if (!textarea) {
70
+ return;
71
+ }
72
+ setCursor(textarea.selectionStart ?? textarea.value.length);
73
+ }
74
+ function selectSuggestion(suggestion) {
75
+ if (!mentionMatch || !hasSuggestionId(suggestion)) {
76
+ return;
77
+ }
78
+ const suggestionId = suggestion.id.trim();
79
+ const nextMessage = `${message.slice(0, mentionMatch.start)}@${suggestionId} ${message.slice(mentionMatch.end)}`;
80
+ const nextCursor = mentionMatch.start + suggestionId.length + 2;
81
+ setMessage(nextMessage);
82
+ setCursor(nextCursor);
83
+ setActiveIndex(0);
84
+ window.requestAnimationFrame(() => {
85
+ const textarea = inputRef.current;
86
+ if (!textarea) {
87
+ return;
88
+ }
89
+ textarea.focus();
90
+ textarea.setSelectionRange(nextCursor, nextCursor);
91
+ });
92
+ }
93
+ return (_jsxs("div", { className: "pai-card pai-chat-composer", children: [_jsxs("div", { className: "pai-chat-composer-row", children: [_jsx("textarea", { ref: inputRef, rows: 2, value: message, disabled: disabled, className: "pai-textarea pai-chat-input", onChange: (event) => {
94
+ setMessage(event.target.value);
95
+ setCursor(event.target.selectionStart ?? event.target.value.length);
96
+ }, onClick: syncCursor, onSelect: syncCursor, onKeyUp: syncCursor, onKeyDown: (event) => {
97
+ if (suggestionsOpen) {
98
+ if (event.key === "ArrowDown") {
99
+ event.preventDefault();
100
+ setActiveIndex((current) => (filteredSuggestions.length ? (current + 1) % filteredSuggestions.length : 0));
101
+ return;
102
+ }
103
+ if (event.key === "ArrowUp") {
104
+ event.preventDefault();
105
+ setActiveIndex((current) => (filteredSuggestions.length ? (current - 1 + filteredSuggestions.length) % filteredSuggestions.length : 0));
106
+ return;
107
+ }
108
+ if (event.key === "Escape") {
109
+ event.preventDefault();
110
+ const textarea = inputRef.current;
111
+ setCursor(textarea?.value.length ?? message.length + 1);
112
+ return;
113
+ }
114
+ if (event.key === "Enter") {
115
+ event.preventDefault();
116
+ if (filteredSuggestions[activeIndex]) {
117
+ selectSuggestion(filteredSuggestions[activeIndex]);
118
+ }
119
+ return;
120
+ }
121
+ }
122
+ if (event.key === "Enter" && !event.shiftKey) {
123
+ event.preventDefault();
124
+ void submit();
125
+ }
126
+ }, placeholder: placeholder || "可输入 @ 引用场景、镜头、关键帧、角色、道具、地点等实体" }), _jsx("button", { type: "button", className: "pai-button pai-button-primary pai-send-button", disabled: disabled || sending || !message.trim(), onClick: () => void submit(), children: sending ? "发送中" : "发送" })] }), suggestionsOpen ? (_jsx("div", { className: "pai-mention-popover", children: filteredSuggestions.length ? (filteredSuggestions.map((suggestion, index) => (_jsxs("button", { type: "button", className: `pai-mention-item${index === activeIndex ? " is-active" : ""}`, onMouseDown: (event) => {
127
+ event.preventDefault();
128
+ selectSuggestion(suggestion);
129
+ }, children: [_jsx("span", { className: `pai-mention-type is-${suggestion.type}`, children: ENTITY_TYPE_LABELS[suggestion.type] }), _jsxs("span", { className: "pai-mention-body", children: [_jsxs("span", { className: "pai-mention-id", children: ["@", suggestion.id] }), suggestion.context ? _jsx("span", { className: "pai-mention-context", children: suggestion.context }) : null] })] }, `${suggestion.type}:${suggestion.id}`)))) : (_jsx("div", { className: "pai-mention-empty", children: "\u6CA1\u6709\u5339\u914D\u7684\u5B9E\u4F53" })) })) : null] }));
130
+ }
131
+ function suggestionMatchesQuery(suggestion, query) {
132
+ if (suggestion.id.toLowerCase().startsWith(query)) {
133
+ return true;
134
+ }
135
+ const searchable = [suggestion.context, suggestion.searchText].filter(Boolean).join(" ").toLowerCase();
136
+ return searchable.includes(query);
137
+ }
138
+ //# sourceMappingURL=ChatComposer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatComposer.js","sourceRoot":"","sources":["../../src/components/ChatComposer.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { JsonRecord } from "../types";
2
+ type EntityTerms = Record<string, {
3
+ zh?: string;
4
+ label?: string;
5
+ }>;
6
+ export type EntityTreeEditorProps = {
7
+ data: JsonRecord;
8
+ saving?: boolean;
9
+ onSave: (data: JsonRecord) => Promise<void>;
10
+ terms?: EntityTerms;
11
+ fieldOptions?: Record<string, string[]>;
12
+ arrayItemTemplates?: Record<string, unknown>;
13
+ alwaysEditing?: boolean;
14
+ defaultEditing?: boolean;
15
+ initialDraft?: JsonRecord;
16
+ initialExpandedPaths?: string[];
17
+ onChange?: (data: JsonRecord) => void;
18
+ };
19
+ export declare function EntityTreeEditor({ data, saving, onSave, terms, fieldOptions, arrayItemTemplates, alwaysEditing, defaultEditing, initialDraft, initialExpandedPaths, onChange, }: EntityTreeEditorProps): import("react").JSX.Element;
20
+ export declare function hasContainerChildren(value: unknown): boolean;
21
+ export {};
22
+ //# sourceMappingURL=EntityTreeEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntityTreeEditor.d.ts","sourceRoot":"","sources":["../../src/components/EntityTreeEditor.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAU3C,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEnE,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACvC,CAAC;AAsQF,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,MAAc,EACd,MAAM,EACN,KAAU,EACV,YAAiB,EACjB,kBAAuB,EACvB,aAAqB,EACrB,cAAsB,EACtB,YAAY,EACZ,oBAAyB,EACzB,QAAQ,GACT,EAAE,qBAAqB,+BAmHvB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,WAIlD"}
@@ -0,0 +1,178 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { cloneValue, parseJsonPointer, removeValueAtPath, setValueAtPath, unescapeJsonPointerPart, } from "../utils/jsonPointer";
4
+ function getLabel(key, path, terms) {
5
+ return terms[path]?.zh ?? terms[path]?.label ?? key;
6
+ }
7
+ function getOptions(path, fieldOptions) {
8
+ return fieldOptions[path];
9
+ }
10
+ function createEmptyLike(value, path, fieldOptions) {
11
+ const options = getOptions(path, fieldOptions);
12
+ if (options?.length)
13
+ return options[0];
14
+ if (Array.isArray(value))
15
+ return [];
16
+ if (value && typeof value === "object") {
17
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, createEmptyLike(child, `${path}.${key}`, fieldOptions)]));
18
+ }
19
+ if (typeof value === "number")
20
+ return 0;
21
+ if (typeof value === "boolean")
22
+ return false;
23
+ return "";
24
+ }
25
+ function getDefaultArrayItem(path, sample, arrayItemTemplates, fieldOptions) {
26
+ const template = arrayItemTemplates[path];
27
+ if (template !== undefined)
28
+ return cloneValue(template);
29
+ if (sample !== undefined)
30
+ return createEmptyLike(sample, `${path}[]`, fieldOptions);
31
+ const options = getOptions(`${path}[]`, fieldOptions);
32
+ if (options?.length)
33
+ return options[0];
34
+ return "";
35
+ }
36
+ function expandedKeysFromJsonPointers(data, pointers) {
37
+ const keys = new Set();
38
+ for (const pointer of pointers) {
39
+ if (!pointer || pointer === "/")
40
+ continue;
41
+ const parts = pointer
42
+ .split("/")
43
+ .slice(1)
44
+ .map(unescapeJsonPointerPart);
45
+ let cursor = data;
46
+ const path = [];
47
+ let displayPath = "";
48
+ for (const part of parts) {
49
+ if (Array.isArray(cursor)) {
50
+ const index = Number(part);
51
+ if (!Number.isInteger(index) || index < 0 || index >= cursor.length)
52
+ break;
53
+ displayPath = `${displayPath}[]`;
54
+ path.push(index);
55
+ cursor = cursor[index];
56
+ }
57
+ else if (cursor && typeof cursor === "object" && part in cursor) {
58
+ displayPath = displayPath ? `${displayPath}.${part}` : part;
59
+ path.push(part);
60
+ cursor = cursor[part];
61
+ }
62
+ else {
63
+ break;
64
+ }
65
+ keys.add(nodeKey(displayPath, path));
66
+ }
67
+ }
68
+ return [...keys];
69
+ }
70
+ function renderValue(value) {
71
+ if (typeof value === "boolean")
72
+ return value ? "true" : "false";
73
+ if (Array.isArray(value))
74
+ return value.join(", ");
75
+ if (value == null || value === "")
76
+ return "-";
77
+ return String(value);
78
+ }
79
+ function nodeKey(displayPath, path) {
80
+ return `${displayPath}:${path.join(".")}`;
81
+ }
82
+ function isContainer(value) {
83
+ return Array.isArray(value) || Boolean(value && typeof value === "object");
84
+ }
85
+ function primitiveInputType(value) {
86
+ if (typeof value === "number")
87
+ return "number";
88
+ return "text";
89
+ }
90
+ function TreeNode({ keyName, value, displayPath, path, removable, editing, expandedKeys, onToggle, onChange, onRemove, onAdd, terms, fieldOptions, }) {
91
+ const label = getLabel(keyName, displayPath, terms);
92
+ const key = nodeKey(displayPath, path);
93
+ const expanded = expandedKeys.has(key);
94
+ if (Array.isArray(value)) {
95
+ return (_jsxs("div", { className: "pai-tree-node", children: [_jsxs("div", { className: "pai-tree-row", title: displayPath, children: [_jsx("button", { type: "button", className: "pai-tree-toggle", onClick: () => onToggle(key), "aria-expanded": expanded, children: expanded ? "▾" : "▸" }), _jsxs("span", { className: "pai-tree-label", children: [label, " (", value.length, ")"] }), editing && removable ? (_jsx("button", { type: "button", className: "pai-icon-button", onClick: () => onRemove(path), "aria-label": `删除 ${label}`, children: "\u5220\u9664" })) : null] }), expanded ? (_jsxs("div", { className: "pai-tree-children", children: [value.map((item, index) => (_jsx(TreeNode, { keyName: `[${index + 1}]`, value: item, displayPath: `${displayPath}[]`, path: [...path, index], removable: editing, editing: editing, expandedKeys: expandedKeys, onToggle: onToggle, onChange: onChange, onRemove: onRemove, onAdd: onAdd, terms: terms, fieldOptions: fieldOptions }, `${key}:${index}`))), editing ? (_jsx("button", { type: "button", className: "pai-button pai-button-secondary pai-array-add", onClick: () => onAdd(path, displayPath, value[0]), children: "\u65B0\u589E\u5143\u7D20" })) : null] })) : null] }));
96
+ }
97
+ if (value && typeof value === "object") {
98
+ return (_jsxs("div", { className: "pai-tree-node", children: [_jsxs("div", { className: "pai-tree-row", title: displayPath, children: [_jsx("button", { type: "button", className: "pai-tree-toggle", onClick: () => onToggle(key), "aria-expanded": expanded, children: expanded ? "▾" : "▸" }), _jsx("span", { className: "pai-tree-label", children: label }), editing && removable ? (_jsx("button", { type: "button", className: "pai-icon-button", onClick: () => onRemove(path), "aria-label": `删除 ${label}`, children: "\u5220\u9664" })) : null] }), expanded ? (_jsx("div", { className: "pai-tree-children", children: Object.entries(value).map(([subKey, subValue]) => (_jsx(TreeNode, { keyName: subKey, value: subValue, displayPath: `${displayPath}.${subKey}`, path: [...path, subKey], editing: editing, expandedKeys: expandedKeys, onToggle: onToggle, onChange: onChange, onRemove: onRemove, onAdd: onAdd, terms: terms, fieldOptions: fieldOptions }, `${key}:${subKey}`))) })) : null] }));
99
+ }
100
+ const options = getOptions(displayPath, fieldOptions);
101
+ return (_jsxs("div", { className: "pai-tree-row pai-tree-leaf", title: displayPath, children: [_jsx("span", { className: "pai-tree-label", children: label }), editing ? (_jsx("span", { className: "pai-tree-control", children: options ? (_jsxs("select", { className: "pai-input", value: value == null ? "" : String(value), onChange: (event) => onChange(path, event.target.value), children: [_jsx("option", { value: "", disabled: true, children: "\u8BF7\u9009\u62E9" }), options.map((option) => (_jsx("option", { value: option, children: option }, option)))] })) : typeof value === "boolean" ? (_jsxs("select", { className: "pai-input", value: value ? "true" : "false", onChange: (event) => onChange(path, event.target.value === "true"), children: [_jsx("option", { value: "true", children: "true" }), _jsx("option", { value: "false", children: "false" })] })) : typeof value === "string" && value.length > 100 ? (_jsx("textarea", { className: "pai-textarea", rows: 3, value: value, onChange: (event) => onChange(path, event.target.value) })) : (_jsx("input", { className: "pai-input", type: primitiveInputType(value), value: value == null ? "" : String(value), onChange: (event) => {
102
+ if (typeof value === "number") {
103
+ const next = Number(event.target.value);
104
+ onChange(path, Number.isFinite(next) ? next : event.target.value);
105
+ return;
106
+ }
107
+ onChange(path, event.target.value);
108
+ } })) })) : (_jsx("span", { className: "pai-tree-value", children: renderValue(value) })), editing && removable ? (_jsx("button", { type: "button", className: "pai-icon-button", onClick: () => onRemove(path), "aria-label": `删除 ${label}`, children: "\u5220\u9664" })) : null] }));
109
+ }
110
+ export function EntityTreeEditor({ data, saving = false, onSave, terms = {}, fieldOptions = {}, arrayItemTemplates = {}, alwaysEditing = false, defaultEditing = false, initialDraft, initialExpandedPaths = [], onChange, }) {
111
+ const [draft, setDraft] = useState(() => cloneValue(initialDraft ?? data));
112
+ const [editing, setEditing] = useState(alwaysEditing || defaultEditing);
113
+ const initialSerialized = useMemo(() => JSON.stringify(data), [data]);
114
+ const initialDraftSerialized = useMemo(() => JSON.stringify(initialDraft), [initialDraft]);
115
+ const draftSerialized = useMemo(() => JSON.stringify(draft), [draft]);
116
+ const expandedPathKey = useMemo(() => initialExpandedPaths.join("|"), [initialExpandedPaths]);
117
+ const initialExpandedKeys = useMemo(() => expandedKeysFromJsonPointers(initialDraft ?? data, initialExpandedPaths), [data, expandedPathKey, initialDraft]);
118
+ const [expandedKeys, setExpandedKeys] = useState(() => new Set(initialExpandedKeys));
119
+ useEffect(() => {
120
+ setDraft(cloneValue(initialDraft ?? data));
121
+ }, [initialSerialized, initialDraftSerialized]);
122
+ useEffect(() => {
123
+ setEditing(alwaysEditing || defaultEditing);
124
+ }, [alwaysEditing, defaultEditing]);
125
+ useEffect(() => {
126
+ setExpandedKeys(new Set(initialExpandedKeys));
127
+ }, [initialExpandedKeys]);
128
+ const updateDraft = (updater) => {
129
+ setDraft((current) => {
130
+ const next = updater(current);
131
+ onChange?.(next);
132
+ return next;
133
+ });
134
+ };
135
+ const toggleExpand = (key) => {
136
+ setExpandedKeys((current) => {
137
+ const next = new Set(current);
138
+ if (next.has(key)) {
139
+ next.delete(key);
140
+ }
141
+ else {
142
+ next.add(key);
143
+ }
144
+ return next;
145
+ });
146
+ };
147
+ const rootKeys = useMemo(() => Object.keys(editing || alwaysEditing ? draft : data), [alwaysEditing, data, draft, editing]);
148
+ const source = editing || alwaysEditing ? draft : data;
149
+ const dirty = draftSerialized !== initialSerialized;
150
+ return (_jsxs("div", { className: "pai-tree-card", children: [_jsxs("div", { className: "pai-tree", children: [rootKeys.map((key) => (_jsx(TreeNode, { keyName: key, value: source[key], displayPath: key, path: [key], editing: alwaysEditing || editing, expandedKeys: expandedKeys, onToggle: toggleExpand, onChange: (path, value) => updateDraft((current) => setValueAtPath(current, path, value)), onRemove: (path) => updateDraft((current) => removeValueAtPath(current, path)), onAdd: (path, displayPath, sample) => updateDraft((current) => {
151
+ const target = path.reduce((cursor, segment) => cursor[segment], current);
152
+ if (!Array.isArray(target)) {
153
+ return current;
154
+ }
155
+ return setValueAtPath(current, path, [...target, getDefaultArrayItem(displayPath, sample, arrayItemTemplates, fieldOptions)]);
156
+ }), terms: terms, fieldOptions: fieldOptions }, key))), !rootKeys.length ? _jsx("div", { className: "pai-muted", children: "\u7A7A\u5BF9\u8C61" }) : null] }), alwaysEditing ? null : editing ? (_jsxs("div", { className: "pai-actions", children: [_jsx("button", { type: "button", className: "pai-button pai-button-secondary", onClick: () => {
157
+ setDraft(cloneValue(data));
158
+ setEditing(false);
159
+ }, children: "\u53D6\u6D88" }), _jsx("button", { type: "button", className: "pai-button pai-button-primary", disabled: saving || !dirty, onClick: () => void onSave(draft).then(() => setEditing(false)), children: saving ? "保存中" : "保存" })] })) : (_jsx("div", { className: "pai-actions", children: _jsx("button", { type: "button", className: "pai-button pai-button-secondary", onClick: () => {
160
+ setEditing(true);
161
+ setDraft(cloneValue(initialDraft ?? data));
162
+ const expandedForVisibleContainers = new Set(expandedKeys);
163
+ for (const pointer of initialExpandedPaths) {
164
+ for (const part of parseJsonPointer(pointer)) {
165
+ void part;
166
+ }
167
+ }
168
+ setExpandedKeys(expandedForVisibleContainers);
169
+ }, children: "\u7F16\u8F91" }) }))] }));
170
+ }
171
+ export function hasContainerChildren(value) {
172
+ if (Array.isArray(value))
173
+ return value.some(isContainer);
174
+ if (value && typeof value === "object")
175
+ return Object.values(value).some(isContainer);
176
+ return false;
177
+ }
178
+ //# sourceMappingURL=EntityTreeEditor.js.map