@world-engines/authoring-ui 0.1.0-alpha.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.
- package/LICENSE +46 -0
- package/dist/DesktopAuthoringShell.d.ts +178 -0
- package/dist/DesktopAuthoringShell.js +235 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +12 -0
- package/dist/ladybug/BrowserGraphCanvasAdapter.d.ts +12 -0
- package/dist/ladybug/BrowserGraphCanvasAdapter.js +8 -0
- package/dist/ladybug/GraphCanvas.d.ts +121 -0
- package/dist/ladybug/GraphCanvas.js +1439 -0
- package/dist/ladybug/GraphEdge.d.ts +26 -0
- package/dist/ladybug/GraphEdge.js +41 -0
- package/dist/ladybug/LadybugExplorer.d.ts +58 -0
- package/dist/ladybug/LadybugExplorer.js +83 -0
- package/dist/ladybug/LadybugSurfaces.d.ts +25 -0
- package/dist/ladybug/LadybugSurfaces.js +9 -0
- package/dist/ladybug/LadybugTable.d.ts +15 -0
- package/dist/ladybug/LadybugTable.js +26 -0
- package/dist/ladybug/entity-props.d.ts +98 -0
- package/dist/ladybug/entity-props.js +363 -0
- package/dist/ladybug/graph-edge-geometry.d.ts +19 -0
- package/dist/ladybug/graph-edge-geometry.js +78 -0
- package/dist/ladybug/graph-edge-mask.d.ts +21 -0
- package/dist/ladybug/graph-edge-mask.js +52 -0
- package/dist/ladybug/graph-groups.d.ts +47 -0
- package/dist/ladybug/graph-groups.js +245 -0
- package/dist/ladybug/graph-layout-contract.d.ts +34 -0
- package/dist/ladybug/graph-layout-contract.js +55 -0
- package/dist/ladybug/graph-layout-solver.d.ts +16 -0
- package/dist/ladybug/graph-layout-solver.js +96 -0
- package/dist/ladybug/graph-layout-wasm.d.ts +9 -0
- package/dist/ladybug/graph-layout-wasm.js +88 -0
- package/dist/ladybug/graph-layout-worker-protocol.d.ts +18 -0
- package/dist/ladybug/graph-layout-worker-protocol.js +1 -0
- package/dist/ladybug/graph-layout-worker.d.ts +1 -0
- package/dist/ladybug/graph-layout-worker.js +66 -0
- package/dist/ladybug/graph-layout.d.ts +2 -0
- package/dist/ladybug/graph-layout.js +1 -0
- package/dist/ladybug/graph-layout.wasm +0 -0
- package/dist/ladybug/graph-node-metrics.d.ts +33 -0
- package/dist/ladybug/graph-node-metrics.js +173 -0
- package/dist/ladybug/graph-physics.d.ts +49 -0
- package/dist/ladybug/graph-physics.js +266 -0
- package/dist/ladybug/graph-placement.d.ts +33 -0
- package/dist/ladybug/graph-placement.js +253 -0
- package/dist/ladybug/graph.css +530 -0
- package/dist/ladybug/graph.d.ts +18 -0
- package/dist/ladybug/graph.js +9 -0
- package/dist/ladybug/score-tags.d.ts +11 -0
- package/dist/ladybug/score-tags.js +1 -0
- package/dist/map/MapWorkspace.d.ts +14 -0
- package/dist/map/MapWorkspace.js +9 -0
- package/dist/score/ScoreRadar.d.ts +10 -0
- package/dist/score/ScoreRadar.js +66 -0
- package/dist/score/index.d.ts +3 -0
- package/dist/score/index.js +3 -0
- package/dist/score/radar-index.d.ts +3 -0
- package/dist/score/radar-index.js +2 -0
- package/dist/score/scenario-scoring-features.d.ts +90 -0
- package/dist/score/scenario-scoring-features.js +380 -0
- package/dist/score/scenario-structural-score.d.ts +76 -0
- package/dist/score/scenario-structural-score.js +211 -0
- package/dist/score/score-presentation.d.ts +51 -0
- package/dist/score/score-presentation.js +44 -0
- package/dist/score/score-radar.css +1 -0
- package/dist/sync/DownloadPanel.d.ts +42 -0
- package/dist/sync/DownloadPanel.js +22 -0
- package/dist/sync/SyncSidebar.d.ts +47 -0
- package/dist/sync/SyncSidebar.js +31 -0
- package/dist/sync/UploadPanel.d.ts +64 -0
- package/dist/sync/UploadPanel.js +36 -0
- package/dist/trigger/TriggerBlueprint.d.ts +37 -0
- package/dist/trigger/TriggerBlueprint.js +26 -0
- package/dist/trigger/tmw/TmwBlueprint.d.ts +71 -0
- package/dist/trigger/tmw/TmwBlueprint.js +206 -0
- package/dist/trigger/tmw/TmwFlowLane.d.ts +22 -0
- package/dist/trigger/tmw/TmwFlowLane.js +44 -0
- package/dist/trigger/tmw/TmwSourceLane.d.ts +17 -0
- package/dist/trigger/tmw/TmwSourceLane.js +38 -0
- package/dist/trigger/tmw/TmwTabs.d.ts +20 -0
- package/dist/trigger/tmw/TmwTabs.js +8 -0
- package/dist/trigger/tmw/TmwTraceLane.d.ts +9 -0
- package/dist/trigger/tmw/TmwTraceLane.js +6 -0
- package/dist/trigger/tmw/TriggerDebugTab.d.ts +9 -0
- package/dist/trigger/tmw/TriggerDebugTab.js +25 -0
- package/dist/trigger/tmw/translate.d.ts +3 -0
- package/dist/trigger/tmw/translate.js +2 -0
- package/dist/view/ViewWorkspace.d.ts +19 -0
- package/dist/view/ViewWorkspace.js +8 -0
- package/package.json +89 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
WorldEngine 官方作者工具许可证
|
|
2
|
+
|
|
3
|
+
版权所有 (c) 2026 Nixdorfer。保留所有权利。
|
|
4
|
+
|
|
5
|
+
本仓库的源代码、构建材料及附带资源(统称“本作品”)不是开源软件,
|
|
6
|
+
不适用任何 OSI 认证的开源许可证。
|
|
7
|
+
|
|
8
|
+
一、官方作者工具分发许可
|
|
9
|
+
|
|
10
|
+
版权所有者可以通过其官方网站、npm registry、签名安装包或其他官方渠道,
|
|
11
|
+
复制并分发由本作品构建的 WorldEngine 作者工具及其必要运行资源
|
|
12
|
+
(统称“官方作者工具”)。仅版权所有者或其书面指定的发布者享有此分发权。
|
|
13
|
+
|
|
14
|
+
二、作者用户许可
|
|
15
|
+
|
|
16
|
+
从官方渠道取得官方作者工具的作者用户,可以:
|
|
17
|
+
|
|
18
|
+
1. 安装和运行官方作者工具;
|
|
19
|
+
2. 使用官方作者工具创作、编辑、预览、导入、导出和提交其有权处理的内容;
|
|
20
|
+
3. 为上述使用目的制作合理必要的本地备份副本。
|
|
21
|
+
|
|
22
|
+
三、未授予的权利
|
|
23
|
+
|
|
24
|
+
除第二条明确允许的行为外,本许可证不授予作者用户或其他第三方以下权利:
|
|
25
|
+
|
|
26
|
+
1. 修改、改编、反编译、反汇编或制作官方作者工具的派生作品;
|
|
27
|
+
2. 复制、镜像、转发、再上传、出售、出租、再分发或再许可官方作者工具;
|
|
28
|
+
3. 使用本作品的源代码、构建材料或任何部分开发、提供或训练其他产品或服务;
|
|
29
|
+
4. 删除或规避版权、许可、签名、访问控制或其他权利管理信息。
|
|
30
|
+
|
|
31
|
+
法律强制允许且合同不得排除的权利不受上述限制影响。
|
|
32
|
+
|
|
33
|
+
四、源代码查看
|
|
34
|
+
|
|
35
|
+
第三方可以在已获合法访问权限的范围内查看本作品,用于审查、安全研究或学习参考;
|
|
36
|
+
查看不授予运行、复制、修改、分发、再许可或用于其他产品与服务的权利。
|
|
37
|
+
|
|
38
|
+
五、无担保与责任限制
|
|
39
|
+
|
|
40
|
+
本作品与官方作者工具均按“现状”提供,不附带任何明示或暗示的担保。
|
|
41
|
+
在适用法律允许的最大范围内,版权所有者不对因访问或使用本作品或官方作者工具
|
|
42
|
+
造成的任何损失承担责任。
|
|
43
|
+
|
|
44
|
+
六、终止
|
|
45
|
+
|
|
46
|
+
违反本许可证将立即终止相应的访问与使用权;终止不影响版权所有者已经产生的权利和救济。
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { LadybugExplorer, type LadybugNode } from "./ladybug/LadybugExplorer.js";
|
|
3
|
+
import type { GraphPosition } from "./ladybug/GraphCanvas.js";
|
|
4
|
+
import { type TmwNodeDraft, type TmwSimulationDraft } from "./trigger/tmw/TmwBlueprint.js";
|
|
5
|
+
import { type TmwDiagnostic, type TmwSimulationTraceEntry, type TriggerDocumentV3 } from "@world-engines/tmw";
|
|
6
|
+
import { ViewWorkspace, type ViewFile } from "./view/ViewWorkspace.js";
|
|
7
|
+
import type { SpatialCreateRequest, SpatialRemoveRequest, SpatialUpdateRequest, SpatialWorldSourceV1 } from "@world-engines/spatial-authoring";
|
|
8
|
+
export type { LadybugNode, ViewFile };
|
|
9
|
+
export interface AuthoringUiActionReceipt {
|
|
10
|
+
readonly project_revision: number;
|
|
11
|
+
readonly source_digest: string;
|
|
12
|
+
readonly readback_verified: true;
|
|
13
|
+
}
|
|
14
|
+
export interface CurrentTriggerProjection {
|
|
15
|
+
readonly source: {
|
|
16
|
+
readonly source_revision: number;
|
|
17
|
+
readonly trigger_document: TriggerDocumentV3;
|
|
18
|
+
};
|
|
19
|
+
readonly transaction_id: string | null;
|
|
20
|
+
readonly transaction_status: "idle" | "draft" | "validated" | "simulated" | "committed" | "expired" | "blocked";
|
|
21
|
+
readonly diagnostics: readonly TmwDiagnostic[];
|
|
22
|
+
readonly trace: readonly TmwSimulationTraceEntry[];
|
|
23
|
+
readonly capability_ids: readonly string[];
|
|
24
|
+
readonly layout_positions?: Readonly<Record<string, GraphPosition>>;
|
|
25
|
+
}
|
|
26
|
+
export interface AuthoringUiSnapshot {
|
|
27
|
+
readonly project_id: string;
|
|
28
|
+
readonly projection_revision: number;
|
|
29
|
+
readonly source_readback: {
|
|
30
|
+
readonly project_revision: number;
|
|
31
|
+
readonly source_digest: string;
|
|
32
|
+
};
|
|
33
|
+
readonly ladybug: {
|
|
34
|
+
readonly nodes: readonly LadybugNode[];
|
|
35
|
+
readonly selected_node_id: string | null;
|
|
36
|
+
readonly search_query: string;
|
|
37
|
+
readonly page?: LadybugPageProjection;
|
|
38
|
+
/** interaction layout read/parse 失败时不能回退到可写默认网格。 */
|
|
39
|
+
readonly graph?: {
|
|
40
|
+
readonly capability: "ready" | "unavailable";
|
|
41
|
+
readonly positions?: Readonly<Record<string, GraphPosition>>;
|
|
42
|
+
readonly pending_node_ids?: readonly string[];
|
|
43
|
+
readonly disabled_node_ids?: readonly string[];
|
|
44
|
+
readonly vector_state_by_node_id?: Readonly<Record<string, "ready" | "pending" | "missing">>;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
/** Desktop 和 Browser 同用经过 authority parser 验证的 TMW SourceV3 projection。 */
|
|
48
|
+
readonly trigger: CurrentTriggerProjection;
|
|
49
|
+
readonly view: {
|
|
50
|
+
readonly files: readonly ViewFile[];
|
|
51
|
+
readonly selected_path: string | null;
|
|
52
|
+
};
|
|
53
|
+
/** 空间 source 由 ProjectHost 注入;UI 不创建副本,也不直接持久化。 */
|
|
54
|
+
readonly map: {
|
|
55
|
+
readonly source: SpatialWorldSourceV1;
|
|
56
|
+
readonly selected_object_id: string | null;
|
|
57
|
+
};
|
|
58
|
+
readonly sync: {
|
|
59
|
+
readonly status: "idle" | "syncing" | "conflict" | "error" | "not_configured";
|
|
60
|
+
readonly detail: string | null;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* 只来自当前 ProjectHost source readback 的本地结构投影。它不是审核结果,
|
|
64
|
+
* quality_factor/base_score 在本地未获得正式审核回执时必须保持 null。
|
|
65
|
+
*/
|
|
66
|
+
readonly structural_score?: DesktopStructuralScoreProjection;
|
|
67
|
+
}
|
|
68
|
+
export interface DesktopStructuralScoreProjection {
|
|
69
|
+
readonly status: "ready" | "unavailable";
|
|
70
|
+
readonly source_revision: number;
|
|
71
|
+
readonly source_digest: string;
|
|
72
|
+
readonly structural_score: number | null;
|
|
73
|
+
readonly quality_factor: null;
|
|
74
|
+
readonly base_score: null;
|
|
75
|
+
readonly primary_type: string | null;
|
|
76
|
+
readonly dims: Readonly<{
|
|
77
|
+
entity_count: number;
|
|
78
|
+
relation_count: number;
|
|
79
|
+
attr_richness: number;
|
|
80
|
+
lore_completeness: number;
|
|
81
|
+
text_volume: number;
|
|
82
|
+
asset_refs: number;
|
|
83
|
+
}> | null;
|
|
84
|
+
readonly evidence: Readonly<{
|
|
85
|
+
entity_count: number;
|
|
86
|
+
relation_count: number;
|
|
87
|
+
total_prop_keys: number;
|
|
88
|
+
}> | null;
|
|
89
|
+
readonly reason: string | null;
|
|
90
|
+
/** 正式远端 draft-preview caller 的最小、已净化 ProjectHost readback 输入。 */
|
|
91
|
+
readonly draft_context: DesktopScoreDraftContext | null;
|
|
92
|
+
}
|
|
93
|
+
export interface DesktopScoreDraftContext {
|
|
94
|
+
readonly basis: "project_host_source_readback";
|
|
95
|
+
readonly client_revision: number;
|
|
96
|
+
readonly source_digest: string;
|
|
97
|
+
readonly description: string;
|
|
98
|
+
readonly entities: ReadonlyArray<Readonly<Record<string, unknown>>>;
|
|
99
|
+
readonly relations: ReadonlyArray<Readonly<Record<string, unknown>>>;
|
|
100
|
+
}
|
|
101
|
+
export type LadybugPageProjection = Readonly<{
|
|
102
|
+
capability: "unavailable";
|
|
103
|
+
reason: string;
|
|
104
|
+
}> | Readonly<{
|
|
105
|
+
capability: "ready";
|
|
106
|
+
mode: "browse" | "search";
|
|
107
|
+
item_ids: readonly string[];
|
|
108
|
+
cursor: string | null;
|
|
109
|
+
next_cursor: string | null;
|
|
110
|
+
previous_cursor: string | null;
|
|
111
|
+
has_previous: boolean;
|
|
112
|
+
total_count: number | null;
|
|
113
|
+
limit: number;
|
|
114
|
+
order: "id_asc" | "relevance";
|
|
115
|
+
source_revision: number;
|
|
116
|
+
source_digest: string;
|
|
117
|
+
}>;
|
|
118
|
+
export interface AuthoringUiAdapter {
|
|
119
|
+
readSnapshot(): Promise<AuthoringUiSnapshot>;
|
|
120
|
+
subscribe?(listener: () => void): () => void;
|
|
121
|
+
selectLadybugNode(node_id: string): Promise<AuthoringUiActionReceipt>;
|
|
122
|
+
searchLadybug(query: string): Promise<AuthoringUiActionReceipt>;
|
|
123
|
+
browseLadybugPage?(direction: "first" | "next" | "previous"): Promise<AuthoringUiActionReceipt>;
|
|
124
|
+
saveLadybugEntity(entity: {
|
|
125
|
+
readonly node_id: string;
|
|
126
|
+
readonly kind: string;
|
|
127
|
+
readonly attributes: Readonly<Record<string, unknown>>;
|
|
128
|
+
}): Promise<AuthoringUiActionReceipt>;
|
|
129
|
+
createLadybugRelation?(source_id: string, target_id: string, kind: string): Promise<AuthoringUiActionReceipt>;
|
|
130
|
+
deleteLadybugRelation?(source_id: string, target_id: string, kind: string): Promise<AuthoringUiActionReceipt>;
|
|
131
|
+
deleteLadybugEntity?(node_id: string): Promise<AuthoringUiActionReceipt>;
|
|
132
|
+
unsetLadybugEntityAttribute?(node_id: string, key: string): Promise<AuthoringUiActionReceipt>;
|
|
133
|
+
commitLadybugPositions?(positions: Readonly<Record<string, GraphPosition>>): Promise<AuthoringUiActionReceipt>;
|
|
134
|
+
validateTrigger(): Promise<AuthoringUiActionReceipt>;
|
|
135
|
+
simulateTrigger(draft?: TmwSimulationDraft): Promise<AuthoringUiActionReceipt>;
|
|
136
|
+
beginTrigger(): Promise<AuthoringUiActionReceipt>;
|
|
137
|
+
commitTrigger(): Promise<AuthoringUiActionReceipt>;
|
|
138
|
+
addTriggerNode(draft: TmwNodeDraft): Promise<AuthoringUiActionReceipt>;
|
|
139
|
+
updateTriggerNode(node: import("@world-engines/tmw").TriggerNodeV3): Promise<AuthoringUiActionReceipt>;
|
|
140
|
+
deleteTriggerNode(stable_id: string): Promise<AuthoringUiActionReceipt>;
|
|
141
|
+
connectTriggerNodes(from_stable_id: string, to_stable_id: string): Promise<AuthoringUiActionReceipt>;
|
|
142
|
+
disconnectTriggerNodes(from_stable_id: string, to_stable_id: string): Promise<AuthoringUiActionReceipt>;
|
|
143
|
+
commitTriggerPositions(positions: Readonly<Record<string, GraphPosition>>): Promise<AuthoringUiActionReceipt>;
|
|
144
|
+
selectViewFile(path: string): Promise<AuthoringUiActionReceipt>;
|
|
145
|
+
saveViewFile(path: string, content: string): Promise<AuthoringUiActionReceipt>;
|
|
146
|
+
refreshPreview(): Promise<AuthoringUiActionReceipt>;
|
|
147
|
+
selectSpatialObject(object_id: string): Promise<AuthoringUiActionReceipt>;
|
|
148
|
+
/** 所有 spatial mutation 都带 source revision,adapter 成功后必须由同一 authority readback。 */
|
|
149
|
+
createSpatialObject(request: SpatialCreateRequest): Promise<AuthoringUiActionReceipt>;
|
|
150
|
+
updateSpatialObject(request: SpatialUpdateRequest): Promise<AuthoringUiActionReceipt>;
|
|
151
|
+
removeSpatialObject(request: SpatialRemoveRequest): Promise<AuthoringUiActionReceipt>;
|
|
152
|
+
/** Trigger 的空间位置目标只能由 source adapter 进行修复。 */
|
|
153
|
+
repairTriggerLocationTarget(trigger_id: string, spatial_id: string): Promise<AuthoringUiActionReceipt>;
|
|
154
|
+
runSync(): Promise<AuthoringUiActionReceipt>;
|
|
155
|
+
}
|
|
156
|
+
export interface DesktopAuthoringUiSlots {
|
|
157
|
+
readonly graphCanvas?: Parameters<typeof LadybugExplorer>[0]["renderGraphCanvas"];
|
|
158
|
+
readonly inspector?: Parameters<typeof LadybugExplorer>[0]["renderInspector"];
|
|
159
|
+
readonly monaco?: Parameters<typeof ViewWorkspace>[0]["renderMonaco"];
|
|
160
|
+
readonly preview?: () => ReactNode;
|
|
161
|
+
/** 项目认证与同步只由受信桌面 top window 注入。 */
|
|
162
|
+
readonly syncSidebar?: () => ReactNode;
|
|
163
|
+
/**
|
|
164
|
+
* 远端 tag/radar caller 必须由已认证且已绑定 scenario 的 top window 注入。
|
|
165
|
+
* Shell 只提供 ProjectHost identity,不会把本地结构分伪装成远端基准。
|
|
166
|
+
*/
|
|
167
|
+
readonly scoreTagComparison?: (context: Readonly<{
|
|
168
|
+
project_id: string;
|
|
169
|
+
project_revision: number;
|
|
170
|
+
source_digest: string;
|
|
171
|
+
structural_score: DesktopStructuralScoreProjection;
|
|
172
|
+
}>) => ReactNode;
|
|
173
|
+
}
|
|
174
|
+
export interface DesktopAuthoringShellProps {
|
|
175
|
+
readonly adapter: AuthoringUiAdapter;
|
|
176
|
+
readonly slots?: DesktopAuthoringUiSlots;
|
|
177
|
+
}
|
|
178
|
+
export declare function DesktopAuthoringShell({ adapter, slots }: DesktopAuthoringShellProps): JSX.Element;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { LadybugExplorer } from "./ladybug/LadybugExplorer.js";
|
|
4
|
+
import { MapWorkspace } from "./map/MapWorkspace.js";
|
|
5
|
+
import { TmwBlueprint } from "./trigger/tmw/TmwBlueprint.js";
|
|
6
|
+
import { parseTriggerDocumentV3 } from "@world-engines/tmw";
|
|
7
|
+
import { ViewWorkspace } from "./view/ViewWorkspace.js";
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function isStringOrNull(value) {
|
|
12
|
+
return typeof value === "string" || value === null;
|
|
13
|
+
}
|
|
14
|
+
function isStringArray(value) {
|
|
15
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string" && entry.trim().length > 0);
|
|
16
|
+
}
|
|
17
|
+
function isGraphProjection(value) {
|
|
18
|
+
// v0 snapshot 没有 interaction layout;调用方必须把它归一为不可写,而不是默认可写网格。
|
|
19
|
+
if (value === undefined)
|
|
20
|
+
return true;
|
|
21
|
+
if (!isRecord(value) || (value.capability !== "ready" && value.capability !== "unavailable"))
|
|
22
|
+
return false;
|
|
23
|
+
if (value.capability === "ready") {
|
|
24
|
+
if (!isRecord(value.positions))
|
|
25
|
+
return false;
|
|
26
|
+
for (const [id, position] of Object.entries(value.positions)) {
|
|
27
|
+
if (id.trim().length === 0 || !isRecord(position) || !Number.isFinite(position.x) || !Number.isFinite(position.y))
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (value.pending_node_ids !== undefined && !isStringArray(value.pending_node_ids))
|
|
32
|
+
return false;
|
|
33
|
+
if (value.disabled_node_ids !== undefined && !isStringArray(value.disabled_node_ids))
|
|
34
|
+
return false;
|
|
35
|
+
if (value.vector_state_by_node_id !== undefined) {
|
|
36
|
+
if (!isRecord(value.vector_state_by_node_id))
|
|
37
|
+
return false;
|
|
38
|
+
for (const [id, state] of Object.entries(value.vector_state_by_node_id)) {
|
|
39
|
+
if (id.trim().length === 0 || (state !== "ready" && state !== "pending" && state !== "missing"))
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
function isLadybugPageProjection(value, projectionRevision, sourceDigest) {
|
|
46
|
+
if (value === undefined)
|
|
47
|
+
return true;
|
|
48
|
+
if (!isRecord(value))
|
|
49
|
+
return false;
|
|
50
|
+
if (value.capability === "unavailable")
|
|
51
|
+
return typeof value.reason === "string";
|
|
52
|
+
return value.capability === "ready"
|
|
53
|
+
&& (value.mode === "browse" || value.mode === "search")
|
|
54
|
+
&& isStringArray(value.item_ids)
|
|
55
|
+
&& isStringOrNull(value.cursor)
|
|
56
|
+
&& isStringOrNull(value.next_cursor)
|
|
57
|
+
&& isStringOrNull(value.previous_cursor)
|
|
58
|
+
&& typeof value.has_previous === "boolean"
|
|
59
|
+
&& (value.total_count === null || (Number.isSafeInteger(value.total_count) && value.total_count >= 0))
|
|
60
|
+
&& Number.isSafeInteger(value.limit)
|
|
61
|
+
&& value.limit > 0
|
|
62
|
+
&& (value.order === "id_asc" || value.order === "relevance")
|
|
63
|
+
&& value.source_revision === projectionRevision
|
|
64
|
+
&& value.source_digest === sourceDigest;
|
|
65
|
+
}
|
|
66
|
+
function isCurrentTriggerProjection(value, projectionRevision) {
|
|
67
|
+
if (!isRecord(value)
|
|
68
|
+
|| !isRecord(value.source)
|
|
69
|
+
|| value.source.source_revision !== projectionRevision
|
|
70
|
+
|| !Number.isSafeInteger(value.source.source_revision)
|
|
71
|
+
|| value.source.source_revision < 0
|
|
72
|
+
|| !isStringOrNull(value.transaction_id)
|
|
73
|
+
|| !["idle", "draft", "validated", "simulated", "committed", "expired", "blocked"].includes(value.transaction_status)
|
|
74
|
+
|| !Array.isArray(value.diagnostics)
|
|
75
|
+
|| !Array.isArray(value.trace)
|
|
76
|
+
|| !isStringArray(value.capability_ids))
|
|
77
|
+
return false;
|
|
78
|
+
return parseTriggerDocumentV3(value.source.trigger_document).ok;
|
|
79
|
+
}
|
|
80
|
+
function isStructuralScoreProjection(value, projectionRevision, sourceDigest) {
|
|
81
|
+
if (value === undefined)
|
|
82
|
+
return true;
|
|
83
|
+
if (!isRecord(value)
|
|
84
|
+
|| (value.status !== "ready" && value.status !== "unavailable")
|
|
85
|
+
|| value.source_revision !== projectionRevision
|
|
86
|
+
|| value.source_digest !== sourceDigest
|
|
87
|
+
|| value.quality_factor !== null
|
|
88
|
+
|| value.base_score !== null
|
|
89
|
+
|| (value.reason !== null && typeof value.reason !== "string"))
|
|
90
|
+
return false;
|
|
91
|
+
if (value.status === "unavailable") {
|
|
92
|
+
return value.structural_score === null
|
|
93
|
+
&& value.primary_type === null
|
|
94
|
+
&& value.dims === null
|
|
95
|
+
&& value.evidence === null
|
|
96
|
+
&& value.draft_context === null;
|
|
97
|
+
}
|
|
98
|
+
return typeof value.structural_score === "number"
|
|
99
|
+
&& Number.isFinite(value.structural_score)
|
|
100
|
+
&& value.structural_score >= 0
|
|
101
|
+
&& value.structural_score <= 100
|
|
102
|
+
&& typeof value.primary_type === "string"
|
|
103
|
+
&& isRecord(value.dims)
|
|
104
|
+
&& Object.values(value.dims).every((entry) => typeof entry === "number" && Number.isFinite(entry) && entry >= 0)
|
|
105
|
+
&& isRecord(value.evidence)
|
|
106
|
+
&& [value.evidence.entity_count, value.evidence.relation_count, value.evidence.total_prop_keys]
|
|
107
|
+
.every((entry) => Number.isSafeInteger(entry) && entry >= 0)
|
|
108
|
+
&& isRecord(value.draft_context)
|
|
109
|
+
&& value.draft_context.basis === "project_host_source_readback"
|
|
110
|
+
&& value.draft_context.client_revision === projectionRevision
|
|
111
|
+
&& value.draft_context.source_digest === sourceDigest
|
|
112
|
+
&& typeof value.draft_context.description === "string"
|
|
113
|
+
&& Array.isArray(value.draft_context.entities)
|
|
114
|
+
&& Array.isArray(value.draft_context.relations);
|
|
115
|
+
}
|
|
116
|
+
/** adapter 的 TS 声明不是来自桌面 IPC 的运行时证明;坏投影必须先拒绝再渲染。 */
|
|
117
|
+
function snapshotError(snapshot) {
|
|
118
|
+
if (!isRecord(snapshot)
|
|
119
|
+
|| typeof snapshot.project_id !== "string"
|
|
120
|
+
|| typeof snapshot.projection_revision !== "number"
|
|
121
|
+
|| !isRecord(snapshot.source_readback)
|
|
122
|
+
|| typeof snapshot.source_readback.project_revision !== "number"
|
|
123
|
+
|| typeof snapshot.source_readback.source_digest !== "string"
|
|
124
|
+
|| !isRecord(snapshot.ladybug)
|
|
125
|
+
|| !Array.isArray(snapshot.ladybug.nodes)
|
|
126
|
+
|| !isStringOrNull(snapshot.ladybug.selected_node_id)
|
|
127
|
+
// 兼容旧投影的可选 search_query;其余结构字段不能静默降级。
|
|
128
|
+
|| (snapshot.ladybug.search_query !== undefined && typeof snapshot.ladybug.search_query !== "string")
|
|
129
|
+
|| !isLadybugPageProjection(snapshot.ladybug.page, snapshot.projection_revision, snapshot.source_readback.source_digest)
|
|
130
|
+
|| !isGraphProjection(snapshot.ladybug.graph)
|
|
131
|
+
|| !isCurrentTriggerProjection(snapshot.trigger, snapshot.projection_revision)
|
|
132
|
+
|| !isRecord(snapshot.view)
|
|
133
|
+
|| !Array.isArray(snapshot.view.files)
|
|
134
|
+
|| !isStringOrNull(snapshot.view.selected_path)
|
|
135
|
+
|| !isRecord(snapshot.map)
|
|
136
|
+
|| !isRecord(snapshot.map.source)
|
|
137
|
+
|| typeof snapshot.map.source.revision !== "number"
|
|
138
|
+
|| !Array.isArray(snapshot.map.source.objects)
|
|
139
|
+
|| !isStringOrNull(snapshot.map.selected_object_id)
|
|
140
|
+
|| !isRecord(snapshot.sync)
|
|
141
|
+
|| !["idle", "syncing", "conflict", "error", "not_configured"].includes(snapshot.sync.status)
|
|
142
|
+
|| !isStringOrNull(snapshot.sync.detail)) {
|
|
143
|
+
return "本地作者项目快照格式无效;已 fail-closed,手动编辑不可用。";
|
|
144
|
+
}
|
|
145
|
+
if (snapshot.projection_revision !== snapshot.source_readback.project_revision
|
|
146
|
+
|| snapshot.projection_revision !== snapshot.map.source.revision) {
|
|
147
|
+
return "投影 revision 与 source readback 不一致;为避免覆盖真实作者源,手动编辑已暂停。";
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
export function DesktopAuthoringShell({ adapter, slots }) {
|
|
152
|
+
const [snapshot, setSnapshot] = useState(null);
|
|
153
|
+
const [failure, setFailure] = useState(null);
|
|
154
|
+
const [busy, setBusy] = useState(false);
|
|
155
|
+
const [pane, setPane] = useState("ladybug");
|
|
156
|
+
const reloadVersion = useRef(0);
|
|
157
|
+
const reload = useCallback(async () => {
|
|
158
|
+
const ownVersion = reloadVersion.current + 1;
|
|
159
|
+
reloadVersion.current = ownVersion;
|
|
160
|
+
setBusy(true);
|
|
161
|
+
try {
|
|
162
|
+
const next = await adapter.readSnapshot();
|
|
163
|
+
if (reloadVersion.current !== ownVersion)
|
|
164
|
+
return null;
|
|
165
|
+
const validationError = snapshotError(next);
|
|
166
|
+
if (validationError !== null) {
|
|
167
|
+
setFailure(validationError);
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
setSnapshot(next);
|
|
171
|
+
setFailure(null);
|
|
172
|
+
return next;
|
|
173
|
+
}
|
|
174
|
+
catch (cause) {
|
|
175
|
+
if (reloadVersion.current !== ownVersion)
|
|
176
|
+
return null;
|
|
177
|
+
const message = cause instanceof Error ? cause.message : "无法读取本地作者项目快照。";
|
|
178
|
+
setFailure(message);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
if (reloadVersion.current === ownVersion)
|
|
183
|
+
setBusy(false);
|
|
184
|
+
}
|
|
185
|
+
}, [adapter]);
|
|
186
|
+
useEffect(() => { void reload(); }, [reload]);
|
|
187
|
+
useEffect(() => adapter.subscribe?.(() => { void reload(); }), [adapter, reload]);
|
|
188
|
+
const perform = useCallback(async (action) => {
|
|
189
|
+
if (failure !== null)
|
|
190
|
+
return;
|
|
191
|
+
setBusy(true);
|
|
192
|
+
try {
|
|
193
|
+
const receipt = await action();
|
|
194
|
+
if (!receipt.readback_verified)
|
|
195
|
+
throw new Error("adapter 返回了未经 source readback 验证的操作结果。");
|
|
196
|
+
const readback = await reload();
|
|
197
|
+
if (readback === null)
|
|
198
|
+
return;
|
|
199
|
+
if (readback.source_readback.project_revision !== receipt.project_revision
|
|
200
|
+
|| readback.source_readback.source_digest !== receipt.source_digest) {
|
|
201
|
+
throw new Error("操作 receipt 与重新读取的 source readback 不一致;当前快照已保持只读,未将操作视为成功。");
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (cause) {
|
|
205
|
+
setFailure(cause instanceof Error ? cause.message : "桌面作者操作失败。");
|
|
206
|
+
setBusy(false);
|
|
207
|
+
}
|
|
208
|
+
}, [failure, reload]);
|
|
209
|
+
if (snapshot === null) {
|
|
210
|
+
return failure === null
|
|
211
|
+
? _jsx("p", { role: "status", children: "\u6B63\u5728\u8BFB\u53D6\u672C\u5730\u4F5C\u8005\u9879\u76EE\u2026" })
|
|
212
|
+
: _jsxs("main", { "aria-label": "Desktop Authoring Shell", "data-testid": "desktop-authoring-shell", children: [_jsx("p", { role: "alert", children: failure }), _jsx("button", { disabled: busy, onClick: () => void reload(), type: "button", children: "\u91CD\u65B0\u8BFB\u53D6" })] });
|
|
213
|
+
}
|
|
214
|
+
const disabled = busy || failure !== null;
|
|
215
|
+
const structuralScore = isStructuralScoreProjection(snapshot.structural_score, snapshot.projection_revision, snapshot.source_readback.source_digest) ? snapshot.structural_score : undefined;
|
|
216
|
+
const scoreForSlot = structuralScore ?? {
|
|
217
|
+
status: "unavailable",
|
|
218
|
+
source_revision: snapshot.projection_revision,
|
|
219
|
+
source_digest: snapshot.source_readback.source_digest,
|
|
220
|
+
structural_score: null,
|
|
221
|
+
quality_factor: null,
|
|
222
|
+
base_score: null,
|
|
223
|
+
primary_type: null,
|
|
224
|
+
dims: null,
|
|
225
|
+
evidence: null,
|
|
226
|
+
reason: "当前 ProjectHost 未提供匹配 source identity 的可信结构投影",
|
|
227
|
+
draft_context: null,
|
|
228
|
+
};
|
|
229
|
+
return (_jsxs("main", { "aria-label": "Desktop Authoring Shell", "data-testid": "desktop-authoring-shell", children: [_jsxs("header", { children: [_jsx("strong", { children: snapshot.project_id }), _jsxs("span", { children: ["revision ", snapshot.projection_revision] }), _jsxs("span", { children: ["sync: ", snapshot.sync.status] }), _jsx("button", { disabled: busy, onClick: () => void reload(), type: "button", children: "\u91CD\u65B0\u8BFB\u53D6" }), _jsx("button", { disabled: disabled || snapshot.sync.status === "not_configured", onClick: () => void perform(() => adapter.runSync()), type: "button", children: "\u540C\u6B65" })] }), snapshot.sync.detail === null ? null : _jsx("p", { role: "status", children: snapshot.sync.detail }), failure === null ? null : _jsx("p", { role: "alert", children: failure }), slots?.syncSidebar?.(), _jsxs("section", { "aria-label": "\u672C\u5730\u7ED3\u6784\u8BC4\u5206", "data-testid": "desktop-structural-score", children: [_jsx("h2", { children: "\u672C\u5730\u7ED3\u6784\u9884\u4F30" }), structuralScore?.status === "ready" ? (_jsxs(_Fragment, { children: [_jsxs("p", { children: [_jsx("strong", { children: structuralScore.structural_score?.toFixed(1) }), " / 100"] }), _jsxs("p", { children: ["\u7ED3\u6784\u7C7B\u578B\uFF08\u4E0D\u4EE3\u8868\u8D28\u91CF\u7B49\u7EA7\uFF09\uFF1A", structuralScore.primary_type] }), _jsxs("p", { children: ["\u6709\u6548\u8BC1\u636E\uFF1A", structuralScore.evidence?.entity_count, " \u5B9E\u4F53 \u00B7 ", structuralScore.evidence?.relation_count, " \u5173\u7CFB \u00B7 ", structuralScore.evidence?.total_prop_keys, " \u5C5E\u6027"] }), _jsx("p", { children: "\u672A\u5BA1\u6838\uFF1A\u672C\u5730\u6CA1\u6709 quality factor\uFF0C\u4E0D\u751F\u6210\u57FA\u7840\u5206\u6216\u7CFB\u7EDF\u5206\u3002" }), _jsx("dl", { children: Object.entries(structuralScore.dims ?? {}).map(([key, value]) => (_jsxs("div", { children: [_jsx("dt", { children: key }), _jsx("dd", { children: value.toFixed(1) })] }, key))) })] })) : (_jsxs("p", { role: "status", children: ["\u672C\u5730\u7ED3\u6784\u9884\u4F30\u4E0D\u53EF\u7528\uFF1A", scoreForSlot.reason] })), slots?.scoreTagComparison?.({
|
|
230
|
+
project_id: snapshot.project_id,
|
|
231
|
+
project_revision: snapshot.projection_revision,
|
|
232
|
+
source_digest: snapshot.source_readback.source_digest,
|
|
233
|
+
structural_score: scoreForSlot,
|
|
234
|
+
})] }), _jsx("nav", { "aria-label": "\u4F5C\u8005\u5DE5\u4F5C\u533A", children: ["ladybug", "trigger", "view", "map"].map((item) => (_jsx("button", { "aria-pressed": pane === item, onClick: () => setPane(item), type: "button", children: item }, item))) }), pane === "ladybug" ? _jsx(LadybugExplorer, { disabled: disabled, graph: snapshot.ladybug.graph ?? { capability: "unavailable" }, nodes: snapshot.ladybug.nodes, ...(adapter.browseLadybugPage === undefined ? {} : { onBrowsePage: (direction) => perform(() => adapter.browseLadybugPage(direction)) }), ...(snapshot.ladybug.page === undefined ? {} : { page: snapshot.ladybug.page }), ...(adapter.commitLadybugPositions === undefined ? {} : { onCommitGraphPositions: (positions) => perform(() => adapter.commitLadybugPositions(positions)) }), ...(adapter.createLadybugRelation === undefined ? {} : { onCreateRelation: (source, target, kind) => perform(() => adapter.createLadybugRelation(source, target, kind)) }), ...(adapter.deleteLadybugRelation === undefined ? {} : { onDeleteRelation: (source, target, kind) => perform(() => adapter.deleteLadybugRelation(source, target, kind)) }), ...(adapter.deleteLadybugEntity === undefined ? {} : { onDeleteEntity: (id) => perform(() => adapter.deleteLadybugEntity(id)) }), ...(adapter.unsetLadybugEntityAttribute === undefined ? {} : { onUnsetAttribute: (id, key) => perform(() => adapter.unsetLadybugEntityAttribute(id, key)) }), onSearch: (query) => perform(() => adapter.searchLadybug(query)), onSaveEntity: (entity) => perform(() => adapter.saveLadybugEntity(entity)), onSelect: (id) => perform(() => adapter.selectLadybugNode(id)), search_query: snapshot.ladybug.search_query, selected_node_id: snapshot.ladybug.selected_node_id, spatialObjects: snapshot.map.source.objects, ...(slots?.graphCanvas === undefined ? {} : { renderGraphCanvas: slots.graphCanvas }), ...(slots?.inspector === undefined ? {} : { renderInspector: slots.inspector }) }) : null, pane === "trigger" ? _jsx(TmwBlueprint, { busy: disabled, capabilityIds: snapshot.trigger.capability_ids, diagnostics: snapshot.trigger.diagnostics, layoutPositions: snapshot.trigger.layout_positions ?? {}, onAddNode: (draft) => perform(() => adapter.addTriggerNode(draft)), onBegin: () => void perform(() => adapter.beginTrigger()), onCommit: () => void perform(() => adapter.commitTrigger()), onConnect: (from, to) => perform(() => adapter.connectTriggerNodes(from, to)), onDeleteNode: (id) => perform(() => adapter.deleteTriggerNode(id)), onDisconnect: (from, to) => perform(() => adapter.disconnectTriggerNodes(from, to)), onLayout: (positions) => perform(() => adapter.commitTriggerPositions(positions)), onSimulate: (draft) => void perform(() => adapter.simulateTrigger(draft)), onUpdateNode: (node) => perform(() => adapter.updateTriggerNode(node)), onValidate: () => void perform(() => adapter.validateTrigger()), source: snapshot.trigger.source, trace: snapshot.trigger.trace, transactionId: snapshot.trigger.transaction_id, transactionStatus: snapshot.trigger.transaction_status }) : null, pane === "view" ? _jsx(ViewWorkspace, { disabled: disabled, files: snapshot.view.files, onPreview: () => perform(() => adapter.refreshPreview()), onSaveFile: (path, content) => perform(() => adapter.saveViewFile(path, content)), onSelectFile: (path) => perform(() => adapter.selectViewFile(path)), selected_path: snapshot.view.selected_path, ...(slots?.monaco === undefined ? {} : { renderMonaco: slots.monaco }), ...(slots?.preview === undefined ? {} : { renderPreview: slots.preview }) }) : null, pane === "map" ? _jsx(MapWorkspace, { disabled: disabled, onCreateObject: (request) => perform(() => adapter.createSpatialObject(request)), onRemoveObject: (request) => perform(() => adapter.removeSpatialObject(request)), onSelectObject: (objectId) => perform(() => adapter.selectSpatialObject(objectId)), onUpdateObject: (request) => perform(() => adapter.updateSpatialObject(request)), selected_object_id: snapshot.map.selected_object_id, source: snapshot.map.source }) : null] }));
|
|
235
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { DesktopAuthoringShell } from "./DesktopAuthoringShell.js";
|
|
2
|
+
export type { AuthoringUiActionReceipt, AuthoringUiAdapter, AuthoringUiSnapshot, DesktopAuthoringShellProps, DesktopAuthoringUiSlots, DesktopStructuralScoreProjection, DesktopScoreDraftContext, LadybugPageProjection, LadybugNode, ViewFile, } from "./DesktopAuthoringShell.js";
|
|
3
|
+
export { LadybugExplorer } from "./ladybug/LadybugExplorer.js";
|
|
4
|
+
export { LadybugTable } from "./ladybug/LadybugTable.js";
|
|
5
|
+
export type { LadybugTableProps } from "./ladybug/LadybugTable.js";
|
|
6
|
+
export { SharedGraphCanvas, forceLayout, graphGlyphKind, groupGraphRelations } from "./ladybug/GraphCanvas.js";
|
|
7
|
+
export type { GraphFocusRequest, GraphGlyphKind, GraphPosition, GraphVectorState, GraphViewport, SharedGraphCanvasProps, SharedGraphNode, SharedGraphRelation, SharedGraphRelationGroupSelection } from "./ladybug/GraphCanvas.js";
|
|
8
|
+
export { BrowserGraphCanvasAdapter } from "./ladybug/BrowserGraphCanvasAdapter.js";
|
|
9
|
+
export type { BrowserGraphCanvasAdapterProps } from "./ladybug/BrowserGraphCanvasAdapter.js";
|
|
10
|
+
export { LadybugHistory, LadybugInspector } from "./ladybug/LadybugSurfaces.js";
|
|
11
|
+
export type { LadybugHistoryEntry, LadybugHistoryProps, LadybugInspectorProps } from "./ladybug/LadybugSurfaces.js";
|
|
12
|
+
export { TmwBlueprint } from "./trigger/tmw/TmwBlueprint.js";
|
|
13
|
+
export { TmwFlowLane } from "./trigger/tmw/TmwFlowLane.js";
|
|
14
|
+
export type { TmwBlueprintProps, TmwNodeDraft, TmwSimulationDraft } from "./trigger/tmw/TmwBlueprint.js";
|
|
15
|
+
export type { TmwFlowLaneProps } from "./trigger/tmw/TmwFlowLane.js";
|
|
16
|
+
export type { TmwTranslate } from "./trigger/tmw/translate.js";
|
|
17
|
+
export { ViewWorkspace } from "./view/ViewWorkspace.js";
|
|
18
|
+
export { MapWorkspace } from "./map/MapWorkspace.js";
|
|
19
|
+
export type { MapWorkspaceProps } from "./map/MapWorkspace.js";
|
|
20
|
+
export { SyncSidebar } from "./sync/SyncSidebar.js";
|
|
21
|
+
export type { SyncSidebarAdapter, SyncSidebarSnapshot } from "./sync/SyncSidebar.js";
|
|
22
|
+
export { DownloadPanel } from "./sync/DownloadPanel.js";
|
|
23
|
+
export type { DownloadReplaceUiPlan, DownloadReplaceUiPort } from "./sync/DownloadPanel.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { DesktopAuthoringShell } from "./DesktopAuthoringShell.js";
|
|
2
|
+
export { LadybugExplorer } from "./ladybug/LadybugExplorer.js";
|
|
3
|
+
export { LadybugTable } from "./ladybug/LadybugTable.js";
|
|
4
|
+
export { SharedGraphCanvas, forceLayout, graphGlyphKind, groupGraphRelations } from "./ladybug/GraphCanvas.js";
|
|
5
|
+
export { BrowserGraphCanvasAdapter } from "./ladybug/BrowserGraphCanvasAdapter.js";
|
|
6
|
+
export { LadybugHistory, LadybugInspector } from "./ladybug/LadybugSurfaces.js";
|
|
7
|
+
export { TmwBlueprint } from "./trigger/tmw/TmwBlueprint.js";
|
|
8
|
+
export { TmwFlowLane } from "./trigger/tmw/TmwFlowLane.js";
|
|
9
|
+
export { ViewWorkspace } from "./view/ViewWorkspace.js";
|
|
10
|
+
export { MapWorkspace } from "./map/MapWorkspace.js";
|
|
11
|
+
export { SyncSidebar } from "./sync/SyncSidebar.js";
|
|
12
|
+
export { DownloadPanel } from "./sync/DownloadPanel.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { JSX, ReactNode } from "react";
|
|
2
|
+
import { type SharedGraphCanvasProps } from "./GraphCanvas.js";
|
|
3
|
+
/** Browser 端用此薄 adapter 接回既有 Inspector、import 与 embedding;core 不依赖 browser store。 */
|
|
4
|
+
export interface BrowserGraphCanvasAdapterProps extends SharedGraphCanvasProps {
|
|
5
|
+
/** 既有浏览器 Import 流程的受限入口;未提供时明确禁用,而不是伪造导入。 */
|
|
6
|
+
readonly onOpenImport?: () => void;
|
|
7
|
+
/** 既有浏览器 embedding 批处理的受限入口;GraphCanvas 不直接读取 store。 */
|
|
8
|
+
readonly onRunEmbedding?: () => void;
|
|
9
|
+
/** 生产 Browser 可复用成熟 embedding 控件,而非让 core 直接接触 store。 */
|
|
10
|
+
readonly renderEmbeddingControl?: () => ReactNode;
|
|
11
|
+
}
|
|
12
|
+
export declare function BrowserGraphCanvasAdapter({ onOpenImport, onRunEmbedding, renderEmbeddingControl, ...canvas }: BrowserGraphCanvasAdapterProps): JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SharedGraphCanvas } from "./GraphCanvas.js";
|
|
3
|
+
export function BrowserGraphCanvasAdapter({ onOpenImport, onRunEmbedding, renderEmbeddingControl, ...canvas }) {
|
|
4
|
+
const importEnabled = !canvas.disabled && onOpenImport !== undefined;
|
|
5
|
+
const embeddingEnabled = !canvas.disabled && onRunEmbedding !== undefined;
|
|
6
|
+
const hasBrowserActions = onOpenImport !== undefined || onRunEmbedding !== undefined || renderEmbeddingControl !== undefined;
|
|
7
|
+
return _jsxs("section", { "aria-label": "GraphCanvas Browser adapter", children: [hasBrowserActions ? _jsxs("div", { "aria-label": "GraphCanvas Browser \u64CD\u4F5C", children: [_jsx("button", { disabled: !importEnabled, onClick: () => onOpenImport?.(), type: "button", children: "\u5BFC\u5165\u5B9E\u4F53" }), renderEmbeddingControl?.() ?? _jsx("button", { disabled: !embeddingEnabled, onClick: () => onRunEmbedding?.(), type: "button", children: "\u751F\u6210 embedding" })] }) : null, _jsx(SharedGraphCanvas, { ...canvas, showToolbar: canvas.showToolbar ?? false })] });
|
|
8
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { type MouseEvent } from "react";
|
|
2
|
+
import { type SharedGraphGroup } from "./graph-groups.js";
|
|
3
|
+
import { type GraphContentEntity, type GraphNameAttrPredicate, type GraphNodeMetrics } from "./graph-node-metrics.js";
|
|
4
|
+
import type { GraphLayoutSolverHandle } from "./graph-layout-contract.js";
|
|
5
|
+
export interface GraphPosition {
|
|
6
|
+
readonly x: number;
|
|
7
|
+
readonly y: number;
|
|
8
|
+
}
|
|
9
|
+
export interface GraphViewport {
|
|
10
|
+
readonly x: number;
|
|
11
|
+
readonly y: number;
|
|
12
|
+
readonly zoom: number;
|
|
13
|
+
}
|
|
14
|
+
export interface GraphFocusRequest {
|
|
15
|
+
/** 同一 requestId 只处理一次;重复聚焦同节点时由宿主换新 id。 */
|
|
16
|
+
readonly requestId: string | number;
|
|
17
|
+
readonly nodeId: string;
|
|
18
|
+
readonly zoom?: number;
|
|
19
|
+
readonly durationMs?: number;
|
|
20
|
+
}
|
|
21
|
+
export type GraphVectorState = "ready" | "pending" | "missing";
|
|
22
|
+
export interface SharedGraphNode {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly label: string;
|
|
25
|
+
readonly kind: string;
|
|
26
|
+
readonly disabled?: boolean;
|
|
27
|
+
/** 缺省回退 disabled;显式 false 可让灰态节点仍可选择/右键以重新启用。 */
|
|
28
|
+
readonly interactionDisabled?: boolean;
|
|
29
|
+
readonly pending?: boolean;
|
|
30
|
+
readonly searchMatch?: boolean;
|
|
31
|
+
readonly highlighted?: boolean;
|
|
32
|
+
/** 缺省表示宿主未提供向量事实,不得解释为 ready。 */
|
|
33
|
+
readonly vector?: GraphVectorState;
|
|
34
|
+
/** 作者可见文本 envelope;共享 metrics 不需要完整 editor EntityNode。 */
|
|
35
|
+
readonly content?: GraphContentEntity;
|
|
36
|
+
/** 高成本投影可由宿主预计算;提供时优先于 content。 */
|
|
37
|
+
readonly metrics?: GraphNodeMetrics;
|
|
38
|
+
}
|
|
39
|
+
export interface SharedGraphRelation {
|
|
40
|
+
readonly id: string;
|
|
41
|
+
readonly source: string;
|
|
42
|
+
readonly target: string;
|
|
43
|
+
readonly label: string;
|
|
44
|
+
readonly pending?: boolean;
|
|
45
|
+
readonly disabled?: boolean;
|
|
46
|
+
readonly searchMatch?: boolean;
|
|
47
|
+
}
|
|
48
|
+
export interface SharedGraphRelationGroupSelection {
|
|
49
|
+
readonly pairId: string;
|
|
50
|
+
/** ReactFlow 聚合线使用稳定的无向 canonical 端点。 */
|
|
51
|
+
readonly source: string;
|
|
52
|
+
readonly target: string;
|
|
53
|
+
readonly relationIds: readonly string[];
|
|
54
|
+
/** 原始顺序、source/target 与 label 全部保留,供宿主详情面板消费。 */
|
|
55
|
+
readonly relations: readonly SharedGraphRelation[];
|
|
56
|
+
}
|
|
57
|
+
export interface SharedGraphCanvasProps {
|
|
58
|
+
readonly nodes: readonly SharedGraphNode[];
|
|
59
|
+
readonly relations: readonly SharedGraphRelation[];
|
|
60
|
+
readonly selectedNodeId: string | null;
|
|
61
|
+
readonly selectedRelationId?: string | null;
|
|
62
|
+
/** 关联 FAB hover/focus 的瞬时远端提示;不改变 selection 或 layout。 */
|
|
63
|
+
readonly highlightNodeId?: string | null;
|
|
64
|
+
readonly searchQuery: string;
|
|
65
|
+
readonly disabled: boolean;
|
|
66
|
+
/** Browser 已有成熟 toolbar 时关闭;Desktop 默认展示共享布局操作。 */
|
|
67
|
+
readonly showToolbar?: boolean;
|
|
68
|
+
/** 大图可开启 ReactFlow viewport culling;缺省 false 保持 npm 现有行为。 */
|
|
69
|
+
readonly onlyRenderVisibleElements?: boolean;
|
|
70
|
+
/** canonical 48px node 左上角;圆心固定为 position + 24,不随内容尺寸漂移。 */
|
|
71
|
+
readonly positions?: Readonly<Record<string, GraphPosition>>;
|
|
72
|
+
readonly viewport?: GraphViewport;
|
|
73
|
+
readonly focusRequest?: GraphFocusRequest | null;
|
|
74
|
+
/** 场景/世界稳定 identity;变化时立即废弃旧 Worker 与所有在途布局结果。 */
|
|
75
|
+
readonly graphIdentity?: string | number;
|
|
76
|
+
readonly groups?: readonly SharedGraphGroup[];
|
|
77
|
+
readonly isNameAttrKey?: GraphNameAttrPredicate;
|
|
78
|
+
readonly onSelectNode: (id: string) => void;
|
|
79
|
+
readonly onCreateNodeAt?: (position: GraphPosition) => void;
|
|
80
|
+
readonly onEditNode?: (id: string) => void;
|
|
81
|
+
/** 只请求宿主确认删除;共享 core 不直接删除实体。 */
|
|
82
|
+
readonly onRequestDeleteNode?: (id: string) => void;
|
|
83
|
+
readonly onCreateRelationFromNode?: (id: string) => void;
|
|
84
|
+
readonly onSelectRelation?: (id: string) => void;
|
|
85
|
+
/** 聚合线选择入口;未提供时向后兼容地回退 onSelectRelation(firstId)。 */
|
|
86
|
+
readonly onSelectRelationGroup?: (selection: SharedGraphRelationGroupSelection) => void;
|
|
87
|
+
readonly onEditRelationGroup?: (selection: SharedGraphRelationGroupSelection) => void;
|
|
88
|
+
/** 只请求宿主确认删除;selection 保留全部底层有向关系。 */
|
|
89
|
+
readonly onRequestDeleteRelationGroup?: (selection: SharedGraphRelationGroupSelection) => void;
|
|
90
|
+
/** 解除纯视觉分组但不删除成员;确认与写入由宿主完成。 */
|
|
91
|
+
readonly onRequestUngroup?: (group: SharedGraphGroup) => void;
|
|
92
|
+
readonly onClearSelection?: () => void;
|
|
93
|
+
/** 双击居中由 core 完成,callback 只回传宿主业务意图。 */
|
|
94
|
+
readonly onNodeDoubleClick?: (id: string) => void;
|
|
95
|
+
/** 在一次 pan/zoom 手势结束时回传 viewport,避免逐帧持久化。 */
|
|
96
|
+
readonly onViewportChange?: (viewport: GraphViewport) => void;
|
|
97
|
+
readonly onLayoutError?: (error: Error) => void;
|
|
98
|
+
/** 测试/受限 runtime 可替换 factory;浏览器缺省始终使用共享 WASM Worker。 */
|
|
99
|
+
readonly createLayoutSolver?: () => GraphLayoutSolverHandle;
|
|
100
|
+
/** Browser adapter 可将既有右键菜单接入;core 不拥有任何 store 或删除 authority。 */
|
|
101
|
+
readonly onNodeContextMenu?: (id: string, event: MouseEvent<HTMLDivElement>) => void;
|
|
102
|
+
/** 仅传递关系拖拽意图;宿主决定是否创建关系。 */
|
|
103
|
+
readonly onRelationDragIntent?: (sourceId: string, targetId: string) => void;
|
|
104
|
+
/** 拖拽或 force-layout 完成后一次性提交全部 position。 */
|
|
105
|
+
readonly onCommitPositions?: (positions: Readonly<Record<string, GraphPosition>>) => void;
|
|
106
|
+
/** group rectangles 与成员 positions 的唯一原子持久化 seam;成员关系由中心点投影,不重复存储。 */
|
|
107
|
+
readonly onCommitLayout?: (layout: {
|
|
108
|
+
readonly positions: Readonly<Record<string, GraphPosition>>;
|
|
109
|
+
readonly groups: readonly SharedGraphGroup[];
|
|
110
|
+
}) => void;
|
|
111
|
+
}
|
|
112
|
+
export declare const graphNodeContentTitle: (label: string, metrics: GraphNodeMetrics) => string;
|
|
113
|
+
export type GraphGlyphKind = "person" | "place" | "faction" | "item" | "event" | "neutral";
|
|
114
|
+
export declare const graphGlyphKind: (kind: string) => GraphGlyphKind;
|
|
115
|
+
export declare function groupGraphRelations(relations: readonly SharedGraphRelation[]): SharedGraphRelationGroupSelection[];
|
|
116
|
+
/**
|
|
117
|
+
* 同步执行 editor 成熟 physics tick;坐标输入输出均为 canonical 48px node 左上角。
|
|
118
|
+
* 只返回最终投影,调用方可在一次交互结束后单次持久化。
|
|
119
|
+
*/
|
|
120
|
+
export declare function forceLayout(nodes: readonly SharedGraphNode[], current: Readonly<Record<string, GraphPosition>>, relations?: readonly SharedGraphRelation[], isNameAttrKey?: GraphNameAttrPredicate): Record<string, GraphPosition>;
|
|
121
|
+
export declare function SharedGraphCanvas(props: SharedGraphCanvasProps): JSX.Element;
|