@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.
Files changed (89) hide show
  1. package/LICENSE +46 -0
  2. package/dist/DesktopAuthoringShell.d.ts +178 -0
  3. package/dist/DesktopAuthoringShell.js +235 -0
  4. package/dist/index.d.ts +23 -0
  5. package/dist/index.js +12 -0
  6. package/dist/ladybug/BrowserGraphCanvasAdapter.d.ts +12 -0
  7. package/dist/ladybug/BrowserGraphCanvasAdapter.js +8 -0
  8. package/dist/ladybug/GraphCanvas.d.ts +121 -0
  9. package/dist/ladybug/GraphCanvas.js +1439 -0
  10. package/dist/ladybug/GraphEdge.d.ts +26 -0
  11. package/dist/ladybug/GraphEdge.js +41 -0
  12. package/dist/ladybug/LadybugExplorer.d.ts +58 -0
  13. package/dist/ladybug/LadybugExplorer.js +83 -0
  14. package/dist/ladybug/LadybugSurfaces.d.ts +25 -0
  15. package/dist/ladybug/LadybugSurfaces.js +9 -0
  16. package/dist/ladybug/LadybugTable.d.ts +15 -0
  17. package/dist/ladybug/LadybugTable.js +26 -0
  18. package/dist/ladybug/entity-props.d.ts +98 -0
  19. package/dist/ladybug/entity-props.js +363 -0
  20. package/dist/ladybug/graph-edge-geometry.d.ts +19 -0
  21. package/dist/ladybug/graph-edge-geometry.js +78 -0
  22. package/dist/ladybug/graph-edge-mask.d.ts +21 -0
  23. package/dist/ladybug/graph-edge-mask.js +52 -0
  24. package/dist/ladybug/graph-groups.d.ts +47 -0
  25. package/dist/ladybug/graph-groups.js +245 -0
  26. package/dist/ladybug/graph-layout-contract.d.ts +34 -0
  27. package/dist/ladybug/graph-layout-contract.js +55 -0
  28. package/dist/ladybug/graph-layout-solver.d.ts +16 -0
  29. package/dist/ladybug/graph-layout-solver.js +96 -0
  30. package/dist/ladybug/graph-layout-wasm.d.ts +9 -0
  31. package/dist/ladybug/graph-layout-wasm.js +88 -0
  32. package/dist/ladybug/graph-layout-worker-protocol.d.ts +18 -0
  33. package/dist/ladybug/graph-layout-worker-protocol.js +1 -0
  34. package/dist/ladybug/graph-layout-worker.d.ts +1 -0
  35. package/dist/ladybug/graph-layout-worker.js +66 -0
  36. package/dist/ladybug/graph-layout.d.ts +2 -0
  37. package/dist/ladybug/graph-layout.js +1 -0
  38. package/dist/ladybug/graph-layout.wasm +0 -0
  39. package/dist/ladybug/graph-node-metrics.d.ts +33 -0
  40. package/dist/ladybug/graph-node-metrics.js +173 -0
  41. package/dist/ladybug/graph-physics.d.ts +49 -0
  42. package/dist/ladybug/graph-physics.js +266 -0
  43. package/dist/ladybug/graph-placement.d.ts +33 -0
  44. package/dist/ladybug/graph-placement.js +253 -0
  45. package/dist/ladybug/graph.css +530 -0
  46. package/dist/ladybug/graph.d.ts +18 -0
  47. package/dist/ladybug/graph.js +9 -0
  48. package/dist/ladybug/score-tags.d.ts +11 -0
  49. package/dist/ladybug/score-tags.js +1 -0
  50. package/dist/map/MapWorkspace.d.ts +14 -0
  51. package/dist/map/MapWorkspace.js +9 -0
  52. package/dist/score/ScoreRadar.d.ts +10 -0
  53. package/dist/score/ScoreRadar.js +66 -0
  54. package/dist/score/index.d.ts +3 -0
  55. package/dist/score/index.js +3 -0
  56. package/dist/score/radar-index.d.ts +3 -0
  57. package/dist/score/radar-index.js +2 -0
  58. package/dist/score/scenario-scoring-features.d.ts +90 -0
  59. package/dist/score/scenario-scoring-features.js +380 -0
  60. package/dist/score/scenario-structural-score.d.ts +76 -0
  61. package/dist/score/scenario-structural-score.js +211 -0
  62. package/dist/score/score-presentation.d.ts +51 -0
  63. package/dist/score/score-presentation.js +44 -0
  64. package/dist/score/score-radar.css +1 -0
  65. package/dist/sync/DownloadPanel.d.ts +42 -0
  66. package/dist/sync/DownloadPanel.js +22 -0
  67. package/dist/sync/SyncSidebar.d.ts +47 -0
  68. package/dist/sync/SyncSidebar.js +31 -0
  69. package/dist/sync/UploadPanel.d.ts +64 -0
  70. package/dist/sync/UploadPanel.js +36 -0
  71. package/dist/trigger/TriggerBlueprint.d.ts +37 -0
  72. package/dist/trigger/TriggerBlueprint.js +26 -0
  73. package/dist/trigger/tmw/TmwBlueprint.d.ts +71 -0
  74. package/dist/trigger/tmw/TmwBlueprint.js +206 -0
  75. package/dist/trigger/tmw/TmwFlowLane.d.ts +22 -0
  76. package/dist/trigger/tmw/TmwFlowLane.js +44 -0
  77. package/dist/trigger/tmw/TmwSourceLane.d.ts +17 -0
  78. package/dist/trigger/tmw/TmwSourceLane.js +38 -0
  79. package/dist/trigger/tmw/TmwTabs.d.ts +20 -0
  80. package/dist/trigger/tmw/TmwTabs.js +8 -0
  81. package/dist/trigger/tmw/TmwTraceLane.d.ts +9 -0
  82. package/dist/trigger/tmw/TmwTraceLane.js +6 -0
  83. package/dist/trigger/tmw/TriggerDebugTab.d.ts +9 -0
  84. package/dist/trigger/tmw/TriggerDebugTab.js +25 -0
  85. package/dist/trigger/tmw/translate.d.ts +3 -0
  86. package/dist/trigger/tmw/translate.js +2 -0
  87. package/dist/view/ViewWorkspace.d.ts +19 -0
  88. package/dist/view/ViewWorkspace.js +8 -0
  89. package/package.json +89 -0
@@ -0,0 +1,71 @@
1
+ import type { TmwDiagnostic, TmwSimulationTraceEntry, TriggerNodeKindV3, TriggerNodeV3 } from "@world-engines/tmw";
2
+ import { type TmwTranslate } from "./translate.js";
3
+ export interface TmwNodeDraft {
4
+ readonly kind: TriggerNodeKindV3;
5
+ readonly stableId: string;
6
+ readonly enabled: boolean;
7
+ readonly domain: "logical_seq" | "world_time" | "presentation_seq" | "real_time";
8
+ readonly dueValue: number;
9
+ readonly effectiveValue: number;
10
+ readonly realTimeClock: "monotonic_session" | "active_play" | "wall_clock";
11
+ readonly wallClockPolicy: "elapse_while_suspended" | "active_session_only" | "reject";
12
+ readonly eventType: string;
13
+ readonly hardFact: boolean;
14
+ readonly conditionValue: boolean;
15
+ readonly capabilityId: string;
16
+ readonly argumentsJson: string;
17
+ readonly executionPolicy: "attempt_once" | "ensure_until";
18
+ readonly rewritePolicy: "normal_only" | "rewrite_only" | "normal_and_rewrite";
19
+ readonly priority: number;
20
+ readonly idempotencyKey: string;
21
+ readonly presentationKind: "atmosphere" | "view" | "music";
22
+ readonly presentationIdentity: string;
23
+ readonly presentationRevision: number;
24
+ readonly presentationIntensity: number;
25
+ readonly presentationVisible: boolean;
26
+ readonly preferredResourceId: string;
27
+ }
28
+ export interface TmwSimulationDraft {
29
+ readonly eventType: string;
30
+ readonly eventPayloadJson: string;
31
+ readonly stateJson: string;
32
+ readonly clockDomain: "logical_seq" | "world_time" | "presentation_seq" | "real_time";
33
+ readonly clockValue: number;
34
+ readonly crossingDirection: "forward" | "backward";
35
+ readonly realTimeClock: "monotonic_session" | "active_play" | "wall_clock";
36
+ readonly presentationPlanId: string;
37
+ readonly presentationSegmentId: string;
38
+ }
39
+ export interface TmwBlueprintProps {
40
+ /** 两端都传入同一 SourceV3 trigger 投影;其余 source 字段不属于 UI。 */
41
+ readonly source: {
42
+ readonly source_revision: number;
43
+ readonly trigger_document: import("@world-engines/tmw").TriggerDocumentV3;
44
+ };
45
+ readonly transactionId: string | null;
46
+ readonly transactionStatus: "idle" | "draft" | "validated" | "simulated" | "committed" | "expired" | "blocked";
47
+ readonly diagnostics: ReadonlyArray<TmwDiagnostic>;
48
+ readonly trace: ReadonlyArray<TmwSimulationTraceEntry>;
49
+ readonly busy: boolean;
50
+ readonly onBegin: () => void;
51
+ readonly onValidate: () => void;
52
+ readonly onSimulate: (draft: TmwSimulationDraft) => void;
53
+ readonly onCommit: () => void;
54
+ readonly capabilityIds: ReadonlyArray<string>;
55
+ readonly onAddNode: (draft: TmwNodeDraft) => void;
56
+ readonly onConnect: (fromStableId: string, toStableId: string) => void;
57
+ readonly onDisconnect: (fromStableId: string, toStableId: string) => void;
58
+ readonly onDeleteNode: (stableId: string) => void;
59
+ readonly onUpdateNode: (node: TriggerNodeV3) => void;
60
+ readonly layoutPositions: Readonly<Record<string, {
61
+ readonly x: number;
62
+ readonly y: number;
63
+ }>>;
64
+ readonly onLayout: (positions: Readonly<Record<string, {
65
+ readonly x: number;
66
+ readonly y: number;
67
+ }>>) => void;
68
+ /** Browser 注入现有 i18n;Desktop 使用同一组件的默认 key。 */
69
+ readonly translate?: TmwTranslate;
70
+ }
71
+ export declare function TmwBlueprint(props: TmwBlueprintProps): JSX.Element;
@@ -0,0 +1,206 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { getTmwRuntimeDomainCapability } from "@world-engines/tmw";
4
+ import { BlocksWorkspace, tmwConditionNodeFromWorkspace, tmwConditionNodeToWorkspace } from "@world-engines/blocks-editor";
5
+ import { TriggerDebugTab } from "./TriggerDebugTab.js";
6
+ import { TmwTabs, TmwToggle } from "./TmwTabs.js";
7
+ import { defaultTmwTranslate } from "./translate.js";
8
+ import { TmwFlowLane } from "./TmwFlowLane.js";
9
+ import { TmwSourceLane } from "./TmwSourceLane.js";
10
+ import { TmwTraceLane } from "./TmwTraceLane.js";
11
+ const nodeEnabled = (node) => node.enabled !== false;
12
+ const newDraft = (kind) => {
13
+ const stableId = `${kind}.${crypto.randomUUID().replaceAll("-", "").slice(0, 16)}`;
14
+ return {
15
+ kind,
16
+ stableId,
17
+ enabled: true,
18
+ domain: kind === "presentation" ? "presentation_seq" : "logical_seq",
19
+ dueValue: 0,
20
+ effectiveValue: 0,
21
+ realTimeClock: "active_play",
22
+ wallClockPolicy: "active_session_only",
23
+ eventType: "",
24
+ hardFact: false,
25
+ conditionValue: true,
26
+ capabilityId: "",
27
+ argumentsJson: "{}",
28
+ executionPolicy: "attempt_once",
29
+ rewritePolicy: "normal_and_rewrite",
30
+ priority: 0,
31
+ idempotencyKey: `${stableId}.once`,
32
+ presentationKind: "atmosphere",
33
+ presentationIdentity: "",
34
+ presentationRevision: 1,
35
+ presentationIntensity: 0,
36
+ presentationVisible: true,
37
+ preferredResourceId: "",
38
+ };
39
+ };
40
+ const draftFromNode = (node) => {
41
+ const base = newDraft(node.kind);
42
+ const time = node.effective_time;
43
+ const common = {
44
+ ...base,
45
+ stableId: node.stable_id,
46
+ enabled: nodeEnabled(node),
47
+ domain: time.domain,
48
+ dueValue: time.due_value,
49
+ effectiveValue: time.effective_value,
50
+ realTimeClock: time.real_time_clock ?? "active_play",
51
+ wallClockPolicy: time.wall_clock_policy ?? "active_session_only",
52
+ };
53
+ if (node.kind === "event")
54
+ return { ...common, eventType: node.event_type, hardFact: node.hard_fact };
55
+ if (node.kind === "condition")
56
+ return { ...common, conditionValue: node.expression.op === "literal" ? node.expression.value : true };
57
+ if (node.kind === "effect")
58
+ return { ...common, capabilityId: node.capability_id, argumentsJson: JSON.stringify(node.arguments, null, 2), executionPolicy: node.execution_policy, rewritePolicy: node.rewrite_policy, priority: node.priority, idempotencyKey: node.idempotency_key };
59
+ if (node.payload.kind === "atmosphere")
60
+ return { ...common, presentationKind: "atmosphere", presentationIdentity: node.payload.atmosphere_id ?? "", presentationIntensity: node.payload.intensity };
61
+ if (node.payload.kind === "view")
62
+ return { ...common, presentationKind: "view", presentationIdentity: node.payload.overlay_id, presentationVisible: node.payload.visible };
63
+ return { ...common, presentationKind: "music", presentationIdentity: node.payload.candidate_set_id, presentationRevision: node.payload.candidate_set_revision, preferredResourceId: node.payload.preferred_resource_id ?? "" };
64
+ };
65
+ const stableIdPattern = /^[a-z0-9](?:[a-z0-9._:-]{0,127})$/u;
66
+ const draftIsValid = (draft) => {
67
+ if (!stableIdPattern.test(draft.stableId)
68
+ || !Number.isFinite(draft.dueValue)
69
+ || !Number.isFinite(draft.effectiveValue)
70
+ || ((draft.domain === "logical_seq" || draft.domain === "presentation_seq")
71
+ && (!Number.isSafeInteger(draft.dueValue)
72
+ || draft.dueValue < 0
73
+ || !Number.isSafeInteger(draft.effectiveValue)
74
+ || draft.effectiveValue < 0)))
75
+ return false;
76
+ if (draft.domain === "real_time"
77
+ && draft.realTimeClock !== "wall_clock"
78
+ && draft.wallClockPolicy !== "active_session_only")
79
+ return false;
80
+ if (draft.kind === "event")
81
+ return stableIdPattern.test(draft.eventType);
82
+ if (draft.kind === "condition")
83
+ return true;
84
+ if (draft.kind === "effect") {
85
+ if (!stableIdPattern.test(draft.capabilityId)
86
+ || !stableIdPattern.test(draft.idempotencyKey)
87
+ || !Number.isSafeInteger(draft.priority)
88
+ || draft.priority < 0)
89
+ return false;
90
+ try {
91
+ const parsed = JSON.parse(draft.argumentsJson);
92
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed);
93
+ }
94
+ catch {
95
+ return false;
96
+ }
97
+ }
98
+ if (draft.presentationKind === "atmosphere") {
99
+ return draft.presentationIntensity >= 0 && draft.presentationIntensity <= 1
100
+ && (draft.presentationIdentity === "" || stableIdPattern.test(draft.presentationIdentity));
101
+ }
102
+ if (draft.presentationKind === "view")
103
+ return stableIdPattern.test(draft.presentationIdentity);
104
+ return stableIdPattern.test(draft.presentationIdentity)
105
+ && Number.isSafeInteger(draft.presentationRevision)
106
+ && draft.presentationRevision > 0
107
+ && (draft.preferredResourceId === "" || stableIdPattern.test(draft.preferredResourceId));
108
+ };
109
+ export function TmwBlueprint(props) {
110
+ const t = props.translate ?? defaultTmwTranslate;
111
+ const [lane, setLane] = useState("flow");
112
+ const [debuggerVisible, setDebuggerVisible] = useState(false);
113
+ const [selectedStableId, setSelectedStableId] = useState(null);
114
+ const [nodeDraft, setNodeDraft] = useState(null);
115
+ const [nodeDraftTouched, setNodeDraftTouched] = useState(false);
116
+ const [conditionWorkspaceJson, setConditionWorkspaceJson] = useState("");
117
+ const [conditionWorkspaceError, setConditionWorkspaceError] = useState(null);
118
+ const [simulationDraft, setSimulationDraft] = useState({ eventType: "manual", eventPayloadJson: "{}", stateJson: "{}", clockDomain: "logical_seq", clockValue: 0, crossingDirection: "forward", realTimeClock: "active_play", presentationPlanId: "author.preview", presentationSegmentId: "segment.preview" });
119
+ const simulationDraftValid = (() => {
120
+ try {
121
+ return simulationDraft.eventType.trim().length > 0
122
+ && Number.isFinite(simulationDraft.clockValue)
123
+ && (simulationDraft.clockDomain !== "presentation_seq" || (simulationDraft.presentationPlanId.trim().length > 0 && simulationDraft.presentationSegmentId.trim().length > 0))
124
+ && typeof JSON.parse(simulationDraft.eventPayloadJson) === "object"
125
+ && typeof JSON.parse(simulationDraft.stateJson) === "object";
126
+ }
127
+ catch {
128
+ return false;
129
+ }
130
+ })();
131
+ const patchSimulationDraft = (key, value) => {
132
+ setSimulationDraft((current) => ({ ...current, [key]: value }));
133
+ };
134
+ const { source, diagnostics, trace } = props;
135
+ const productionDomains = getTmwRuntimeDomainCapability("production").produced_clock_domains;
136
+ const unsupportedProductionNodes = [...source.trigger_document.events, ...source.trigger_document.conditions, ...source.trigger_document.effects, ...source.trigger_document.presentations]
137
+ .filter((node) => !productionDomains.includes(node.effective_time.domain));
138
+ const lanes = [
139
+ { id: "source", label: t("tmw.blueprint.lane.source") },
140
+ { id: "flow", label: t("tmw.blueprint.lane.flow") },
141
+ { id: "trace", label: t("tmw.blueprint.lane.trace") },
142
+ ];
143
+ const canRevise = props.transactionId !== null
144
+ && props.transactionStatus !== "committed"
145
+ && props.transactionStatus !== "expired"
146
+ && props.transactionStatus !== "blocked";
147
+ const draftMutable = canRevise && !props.busy;
148
+ const selectedCondition = source.trigger_document.conditions.find((condition) => condition.stable_id === selectedStableId) ?? null;
149
+ const selectedNode = [...source.trigger_document.events, ...source.trigger_document.conditions, ...source.trigger_document.effects, ...source.trigger_document.presentations].find((node) => node.stable_id === selectedStableId) ?? null;
150
+ useEffect(() => {
151
+ if (selectedCondition === null) {
152
+ setConditionWorkspaceJson("");
153
+ setConditionWorkspaceError(null);
154
+ return;
155
+ }
156
+ setConditionWorkspaceJson(JSON.stringify(tmwConditionNodeToWorkspace(selectedCondition)));
157
+ setConditionWorkspaceError(null);
158
+ }, [selectedCondition]);
159
+ const saveConditionWorkspace = () => {
160
+ try {
161
+ const condition = tmwConditionNodeFromWorkspace(JSON.parse(conditionWorkspaceJson));
162
+ props.onUpdateNode({
163
+ ...condition,
164
+ enabled: selectedCondition === null ? true : nodeEnabled(selectedCondition),
165
+ });
166
+ setConditionWorkspaceError(null);
167
+ }
168
+ catch (cause) {
169
+ setConditionWorkspaceError(cause instanceof Error ? cause.message : "tmw_condition_workspace_invalid");
170
+ }
171
+ };
172
+ useEffect(() => {
173
+ if (draftMutable)
174
+ return;
175
+ setNodeDraft(null);
176
+ setNodeDraftTouched(false);
177
+ }, [draftMutable]);
178
+ const openDraft = (kind) => {
179
+ if (!draftMutable)
180
+ return;
181
+ setNodeDraft(newDraft(kind));
182
+ setNodeDraftTouched(false);
183
+ };
184
+ const closeDraft = () => {
185
+ setNodeDraft(null);
186
+ setNodeDraftTouched(false);
187
+ };
188
+ const patchDraft = (patch) => {
189
+ if (!draftMutable)
190
+ return;
191
+ setNodeDraftTouched(true);
192
+ setNodeDraft((current) => current === null ? null : { ...current, ...patch });
193
+ };
194
+ const submitDraft = () => {
195
+ if (!draftMutable || !nodeDraftTouched || nodeDraft === null || !draftIsValid(nodeDraft))
196
+ return;
197
+ props.onAddNode(nodeDraft);
198
+ closeDraft();
199
+ };
200
+ return (_jsxs("main", { className: "tmw-blueprint", "aria-label": t("tmw.blueprint.aria"), "data-tmw-blueprint": "v3", children: [_jsxs("header", { className: "tmw-blueprint__header", children: [_jsxs("div", { className: "tmw-blueprint__identity", children: [_jsx("strong", { children: t("tmw.blueprint.title") }), _jsxs("span", { children: [" \u00B7 ", t("tmw.blueprint.source_revision", undefined, { revision: source.source_revision })] }), _jsxs("span", { children: [" \u00B7 ", t(`tmw.blueprint.status.${props.transactionStatus}`)] }), props.transactionId === null ? null : _jsxs("code", { children: [" \u00B7 ", props.transactionId] })] }), _jsxs("div", { className: "tmw-blueprint__view-controls", children: [_jsx(TmwTabs, { items: lanes, value: lane, onSelect: setLane, ariaLabel: t("tmw.blueprint.lanes"), testId: "tmw-blueprint-lanes" }), _jsx(TmwToggle, { active: debuggerVisible, onToggle: () => setDebuggerVisible((visible) => !visible), label: t("tmw.blueprint.debugger"), testId: "tmw-debugger-toggle" })] })] }), _jsxs("div", { className: "tmw-blueprint__toolbar", role: "toolbar", "aria-label": t("tmw.blueprint.transaction_toolbar"), children: [_jsx("button", { type: "button", disabled: props.busy || (props.transactionId !== null
201
+ && props.transactionStatus !== "committed"
202
+ && props.transactionStatus !== "expired"), onClick: props.onBegin, children: t("tmw.blueprint.action.begin") }), _jsx("button", { type: "button", disabled: !draftMutable || selectedNode === null || selectedNode.kind === "condition", onClick: () => { if (selectedNode !== null) {
203
+ setNodeDraft(draftFromNode(selectedNode));
204
+ setNodeDraftTouched(false);
205
+ } }, children: "\u7F16\u8F91\u9009\u4E2D\u8282\u70B9" }), _jsx("button", { type: "button", disabled: !draftMutable || selectedNode === null, onClick: () => selectedNode !== null && props.onDeleteNode(selectedNode.stable_id), children: "\u5220\u9664\u9009\u4E2D\u8282\u70B9" }), _jsx("button", { type: "button", disabled: props.busy || props.transactionId === null, onClick: props.onValidate, children: t("tmw.blueprint.action.validate") }), _jsx("input", { "aria-label": "\u6A21\u62DF\u4E8B\u4EF6\u7C7B\u578B", value: simulationDraft.eventType, onChange: (event) => patchSimulationDraft("eventType", event.currentTarget.value) }), _jsx("input", { "aria-label": "\u6A21\u62DF\u4E8B\u4EF6 payload JSON", value: simulationDraft.eventPayloadJson, onChange: (event) => patchSimulationDraft("eventPayloadJson", event.currentTarget.value) }), _jsx("input", { "aria-label": "\u6A21\u62DF\u72B6\u6001 JSON", value: simulationDraft.stateJson, onChange: (event) => patchSimulationDraft("stateJson", event.currentTarget.value) }), _jsxs("select", { "aria-label": "\u6A21\u62DF\u65F6\u949F\u57DF", value: simulationDraft.clockDomain, onChange: (event) => patchSimulationDraft("clockDomain", event.currentTarget.value), children: [_jsx("option", { value: "logical_seq", children: "logical_seq" }), _jsx("option", { value: "world_time", children: "world_time" }), _jsx("option", { value: "presentation_seq", children: "presentation_seq" }), _jsx("option", { value: "real_time", children: "real_time" })] }), _jsx("input", { "aria-label": "\u6A21\u62DF\u65F6\u949F\u503C", type: "number", value: simulationDraft.clockValue, onChange: (event) => patchSimulationDraft("clockValue", Number(event.currentTarget.value)) }), _jsxs("select", { "aria-label": "\u6A21\u62DF\u7A7F\u8D8A\u65B9\u5411", value: simulationDraft.crossingDirection, onChange: (event) => patchSimulationDraft("crossingDirection", event.currentTarget.value), children: [_jsx("option", { value: "forward", children: "forward" }), _jsx("option", { value: "backward", children: "backward" })] }), simulationDraft.clockDomain === "real_time" ? _jsxs("select", { "aria-label": "\u6A21\u62DF\u771F\u5B9E\u65F6\u949F", value: simulationDraft.realTimeClock, onChange: (event) => patchSimulationDraft("realTimeClock", event.currentTarget.value), children: [_jsx("option", { value: "active_play", children: "active_play" }), _jsx("option", { value: "monotonic_session", children: "monotonic_session" }), _jsx("option", { value: "wall_clock", children: "wall_clock" })] }) : null, simulationDraft.clockDomain === "presentation_seq" ? _jsxs(_Fragment, { children: [_jsx("input", { "aria-label": "\u6A21\u62DF\u6F14\u51FA plan ID", value: simulationDraft.presentationPlanId, onChange: (event) => patchSimulationDraft("presentationPlanId", event.currentTarget.value) }), _jsx("input", { "aria-label": "\u6A21\u62DF\u6F14\u51FA segment ID", value: simulationDraft.presentationSegmentId, onChange: (event) => patchSimulationDraft("presentationSegmentId", event.currentTarget.value) })] }) : null, _jsx("button", { type: "button", disabled: props.busy || !simulationDraftValid, onClick: () => props.onSimulate(simulationDraft), children: t("tmw.blueprint.action.simulate") }), _jsx("button", { type: "button", disabled: props.busy || props.transactionId === null || (props.transactionStatus !== "validated" && props.transactionStatus !== "simulated"), onClick: props.onCommit, children: t("tmw.blueprint.action.commit") })] }), unsupportedProductionNodes.length === 0 ? null : (_jsxs("div", { className: "tmw-diagnostic", role: "alert", children: ["\u5F53\u524D\u751F\u4EA7\u8FD0\u884C\u65F6\u6CA1\u6709\u4EE5\u4E0B\u65F6\u949F\u57DF\u7684\u6B63\u5F0F producer\uFF0C\u6587\u6863\u53EF\u7EE7\u7EED\u8BFB\u53D6\u548C\u7F16\u8F91\uFF0C\u4F46\u9A8C\u8BC1\u3001\u7F16\u8BD1\u4E0E\u53D1\u5E03\u4E0D\u4F1A ready\uFF1A", unsupportedProductionNodes.map((node) => `${node.stable_id}=${node.effective_time.domain}`).join(",")] })), selectedCondition === null ? null : (_jsxs("section", { className: "tmw-condition-blocks", "aria-label": "Scratch \u6761\u4EF6\u7F16\u8F91\u5668", children: [_jsxs("header", { className: "tmw-lane-heading", children: [_jsxs("div", { children: [_jsx("strong", { children: "Scratch \u6761\u4EF6" }), _jsx("span", { children: " \u00B7 TriggerDocumentV3" })] }), _jsx("button", { type: "button", disabled: !draftMutable || conditionWorkspaceJson.length === 0, onClick: saveConditionWorkspace, children: "\u4FDD\u5B58\u6761\u4EF6" })] }), conditionWorkspaceError === null ? null : _jsx("div", { role: "alert", className: "tmw-diagnostic", children: conditionWorkspaceError }), _jsx(BlocksWorkspace, { schema: "tmw-condition-v3", ownerKey: selectedCondition.stable_id, workspaceJson: conditionWorkspaceJson, ctx: { event_list: [], entity_list: [] }, onWorkspaceJsonChange: setConditionWorkspaceJson }), _jsx("small", { children: "\u81EA\u7136\u8BED\u8A00\u79EF\u6728\u4EA7\u751F\u8BED\u4E49 Judge \u8BF7\u6C42\uFF1B\u4E0D\u4F1A\u628A\u6587\u672C\u76F4\u63A5\u5199\u6210 true\u3002" })] })), nodeDraft === null ? null : (_jsxs("section", { className: "tmw-node-draft", "aria-label": t("tmw.draft.aria"), children: [_jsxs("header", { children: [_jsxs("div", { children: [_jsx("strong", { children: t("tmw.draft.title", undefined, { kind: t(`tmw.palette.${nodeDraft.kind}.label`) }) }), _jsx("span", { children: t("tmw.draft.explicit_hint") })] }), _jsx("button", { type: "button", onClick: closeDraft, "aria-label": t("common.close"), children: "\u00D7" })] }), _jsxs("div", { className: "tmw-node-draft__grid", children: [_jsxs("label", { className: "tmw-node-draft__checkbox", children: [_jsx("input", { type: "checkbox", checked: nodeDraft.enabled, onChange: (event) => patchDraft({ enabled: event.currentTarget.checked }) }), _jsx("span", { children: "\u542F\u7528" })] }), _jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.stable_id") }), _jsx("input", { value: nodeDraft.stableId, readOnly: selectedNode?.stable_id === nodeDraft.stableId, onChange: (event) => patchDraft({ stableId: event.currentTarget.value }) })] }), _jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.domain") }), _jsxs("select", { value: nodeDraft.domain, onChange: (event) => patchDraft({ domain: event.currentTarget.value }), children: [_jsx("option", { value: "logical_seq", children: t("tmw.domain.logical_seq") }), _jsx("option", { value: "world_time", children: t("tmw.domain.world_time") }), _jsx("option", { value: "presentation_seq", children: t("tmw.domain.presentation_seq") }), _jsx("option", { value: "real_time", children: t("tmw.domain.real_time") })] }), !productionDomains.includes(nodeDraft.domain) ? _jsx("small", { role: "status", children: "\u6B64\u57DF\u4EC5\u53EF\u7528\u663E\u5F0F simulation observation \u9884\u89C8\uFF1B\u5F53\u524D\u751F\u4EA7\u7F16\u8BD1\u4F1A\u62D2\u7EDD\u3002" }) : null] }), _jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.due_value") }), _jsx("input", { type: "number", value: nodeDraft.dueValue, onChange: (event) => patchDraft({ dueValue: Number(event.currentTarget.value) }) })] }), _jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.effective_value") }), _jsx("input", { type: "number", value: nodeDraft.effectiveValue, onChange: (event) => patchDraft({ effectiveValue: Number(event.currentTarget.value) }) })] }), nodeDraft.domain === "real_time" ? (_jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.real_time_clock") }), _jsxs("select", { value: nodeDraft.realTimeClock, onChange: (event) => patchDraft({ realTimeClock: event.currentTarget.value }), children: [_jsx("option", { value: "monotonic_session", children: "monotonic_session" }), _jsx("option", { value: "active_play", children: "active_play" }), _jsx("option", { value: "wall_clock", children: "wall_clock" })] })] }), _jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.wall_clock_policy") }), _jsxs("select", { value: nodeDraft.wallClockPolicy, onChange: (event) => patchDraft({ wallClockPolicy: event.currentTarget.value }), children: [_jsx("option", { value: "active_session_only", children: "active_session_only" }), _jsx("option", { value: "elapse_while_suspended", children: "elapse_while_suspended" }), _jsx("option", { value: "reject", children: "reject" })] })] })] })) : null, nodeDraft.kind === "event" ? (_jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.event_type") }), _jsx("input", { value: nodeDraft.eventType, onChange: (event) => patchDraft({ eventType: event.currentTarget.value }) })] }), _jsxs("label", { className: "tmw-node-draft__checkbox", children: [_jsx("input", { type: "checkbox", checked: nodeDraft.hardFact, onChange: (event) => patchDraft({ hardFact: event.currentTarget.checked }) }), _jsx("span", { children: t("tmw.draft.hard_fact") })] })] })) : null, nodeDraft.kind === "condition" ? (_jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.literal_value") }), _jsxs("select", { value: String(nodeDraft.conditionValue), onChange: (event) => patchDraft({ conditionValue: event.currentTarget.value === "true" }), children: [_jsx("option", { value: "true", children: "true" }), _jsx("option", { value: "false", children: "false" })] })] })) : null, nodeDraft.kind === "effect" ? (_jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.capability") }), _jsxs("select", { value: nodeDraft.capabilityId, onChange: (event) => patchDraft({ capabilityId: event.currentTarget.value }), children: [_jsx("option", { value: "", children: t("tmw.draft.choose_capability") }), props.capabilityIds.map((capabilityId) => _jsx("option", { value: capabilityId, children: capabilityId }, capabilityId))] })] }), _jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.arguments") }), _jsx("textarea", { value: nodeDraft.argumentsJson, onChange: (event) => patchDraft({ argumentsJson: event.currentTarget.value }) })] }), _jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.execution_policy") }), _jsxs("select", { value: nodeDraft.executionPolicy, onChange: (event) => patchDraft({ executionPolicy: event.currentTarget.value }), children: [_jsx("option", { value: "attempt_once", children: "attempt_once" }), _jsx("option", { value: "ensure_until", children: "ensure_until" })] })] }), _jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.rewrite_policy") }), _jsxs("select", { value: nodeDraft.rewritePolicy, onChange: (event) => patchDraft({ rewritePolicy: event.currentTarget.value }), children: [_jsx("option", { value: "normal_only", children: "normal_only" }), _jsx("option", { value: "rewrite_only", children: "rewrite_only" }), _jsx("option", { value: "normal_and_rewrite", children: "normal_and_rewrite" })] })] }), _jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.priority") }), _jsx("input", { type: "number", min: "0", step: "1", value: nodeDraft.priority, onChange: (event) => patchDraft({ priority: Number(event.currentTarget.value) }) })] }), _jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.idempotency_key") }), _jsx("input", { value: nodeDraft.idempotencyKey, onChange: (event) => patchDraft({ idempotencyKey: event.currentTarget.value }) })] })] })) : null, nodeDraft.kind === "presentation" ? (_jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.presentation_kind") }), _jsxs("select", { value: nodeDraft.presentationKind, onChange: (event) => patchDraft({ presentationKind: event.currentTarget.value }), children: [_jsx("option", { value: "atmosphere", children: "atmosphere" }), _jsx("option", { value: "view", children: "view" }), _jsx("option", { value: "music", children: "music" })] })] }), _jsxs("label", { children: [_jsx("span", { children: t(`tmw.draft.presentation_identity.${nodeDraft.presentationKind}`) }), _jsx("input", { value: nodeDraft.presentationIdentity, onChange: (event) => patchDraft({ presentationIdentity: event.currentTarget.value }) })] }), nodeDraft.presentationKind === "atmosphere" ? (_jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.intensity") }), _jsx("input", { type: "number", min: "0", max: "1", step: "0.05", value: nodeDraft.presentationIntensity, onChange: (event) => patchDraft({ presentationIntensity: Number(event.currentTarget.value) }) })] })) : null, nodeDraft.presentationKind === "view" ? (_jsxs("label", { className: "tmw-node-draft__checkbox", children: [_jsx("input", { type: "checkbox", checked: nodeDraft.presentationVisible, onChange: (event) => patchDraft({ presentationVisible: event.currentTarget.checked }) }), _jsx("span", { children: t("tmw.draft.visible") })] })) : null, nodeDraft.presentationKind === "music" ? (_jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.candidate_revision") }), _jsx("input", { type: "number", min: "1", step: "1", value: nodeDraft.presentationRevision, onChange: (event) => patchDraft({ presentationRevision: Number(event.currentTarget.value) }) })] }), _jsxs("label", { children: [_jsx("span", { children: t("tmw.draft.preferred_resource") }), _jsx("input", { value: nodeDraft.preferredResourceId, onChange: (event) => patchDraft({ preferredResourceId: event.currentTarget.value }) })] })] })) : null] })) : null] }), _jsxs("footer", { children: [_jsx("button", { type: "button", onClick: closeDraft, children: t("common.cancel") }), _jsx("button", { type: "button", disabled: !draftMutable || !nodeDraftTouched || !draftIsValid(nodeDraft), onClick: submitDraft, children: t("tmw.draft.add") })] })] })), lane === "source" ? (_jsx(TmwSourceLane, { document: source.trigger_document, diagnostics: diagnostics, selectedStableId: selectedStableId, onSelectStableId: setSelectedStableId, canRevise: draftMutable, onAddNode: openDraft, translate: t })) : null, lane === "flow" ? (_jsx(TmwFlowLane, { document: source.trigger_document, selectedStableId: selectedStableId, onSelectStableId: setSelectedStableId, canRevise: draftMutable, onAddNode: openDraft, onConnect: props.onConnect, onDisconnect: props.onDisconnect, onDeleteNode: props.onDeleteNode, layoutPositions: props.layoutPositions, onLayout: props.onLayout, translate: t })) : null, lane === "trace" ? (_jsx(TmwTraceLane, { trace: trace, selectedStableId: selectedStableId, onSelectStableId: setSelectedStableId, translate: t })) : null, debuggerVisible ? (_jsx("div", { className: "tmw-blueprint__debugger", children: _jsx(TriggerDebugTab, { document: source.trigger_document, diagnostics: diagnostics, trace: trace, selectedStableId: selectedStableId, onSelectStableId: setSelectedStableId }) })) : null] }));
206
+ }
@@ -0,0 +1,22 @@
1
+ import type { TriggerDocumentV3, TriggerNodeKindV3 } from "@world-engines/tmw";
2
+ import { type TmwTranslate } from "./translate.js";
3
+ export interface TmwFlowLaneProps {
4
+ readonly document: TriggerDocumentV3;
5
+ readonly selectedStableId: string | null;
6
+ readonly onSelectStableId: (stableId: string) => void;
7
+ readonly canRevise: boolean;
8
+ readonly onAddNode: (kind: TriggerNodeKindV3) => void;
9
+ readonly onConnect: (fromStableId: string, toStableId: string) => void;
10
+ readonly onDisconnect: (fromStableId: string, toStableId: string) => void;
11
+ readonly onDeleteNode: (stableId: string) => void;
12
+ readonly layoutPositions: Readonly<Record<string, {
13
+ readonly x: number;
14
+ readonly y: number;
15
+ }>>;
16
+ readonly onLayout: (positions: Readonly<Record<string, {
17
+ readonly x: number;
18
+ readonly y: number;
19
+ }>>) => void;
20
+ readonly translate?: TmwTranslate;
21
+ }
22
+ export declare function TmwFlowLane({ document, selectedStableId, onSelectStableId, canRevise, onAddNode, onConnect, onDisconnect, onDeleteNode, layoutPositions, onLayout, translate, }: TmwFlowLaneProps): JSX.Element;
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import ReactFlow, { Background, BackgroundVariant, Controls, MarkerType, MiniMap, applyNodeChanges, } from "reactflow";
4
+ import { useEffect, useState } from "react";
5
+ import { TMW_NODE_PALETTE } from "./TmwSourceLane.js";
6
+ import { defaultTmwTranslate } from "./translate.js";
7
+ export function TmwFlowLane({ document, selectedStableId, onSelectStableId, canRevise, onAddNode, onConnect, onDisconnect, onDeleteNode, layoutPositions, onLayout, translate, }) {
8
+ const t = translate ?? defaultTmwTranslate;
9
+ const graph = useMemo(() => {
10
+ const groups = [
11
+ document.events,
12
+ document.conditions,
13
+ document.effects,
14
+ document.presentations,
15
+ ];
16
+ const nodes = groups.flatMap((items, column) => items.map((node, row) => ({
17
+ id: node.stable_id,
18
+ position: layoutPositions[node.stable_id] ?? { x: 80 + column * 280, y: 70 + row * 150 },
19
+ data: {
20
+ label: (_jsxs("div", { className: `tmw-flow-node tmw-flow-node--${node.kind}`, children: [_jsx("span", { children: node.kind }), _jsx("strong", { children: node.stable_id }), _jsx("small", { children: node.effective_time.domain })] })),
21
+ },
22
+ selected: selectedStableId === node.stable_id,
23
+ className: `tmw-reactflow-node tmw-reactflow-node--${node.kind}`,
24
+ })));
25
+ const edges = document.edges.map((edge) => ({
26
+ id: `${edge.from_stable_id}->${edge.to_stable_id}`,
27
+ source: edge.from_stable_id,
28
+ target: edge.to_stable_id,
29
+ markerEnd: { type: MarkerType.ArrowClosed },
30
+ animated: false,
31
+ }));
32
+ return { nodes, edges };
33
+ }, [document, layoutPositions, selectedStableId]);
34
+ const [nodes, setNodes] = useState(graph.nodes);
35
+ useEffect(() => setNodes(graph.nodes), [graph.nodes]);
36
+ const handleNodesChange = (changes) => setNodes((current) => applyNodeChanges(changes, current));
37
+ const persistLayout = () => onLayout(Object.fromEntries(nodes.map((node) => [node.id, node.position])));
38
+ const handleConnect = (connection) => {
39
+ if (connection.source === null || connection.target === null)
40
+ return;
41
+ onConnect(connection.source, connection.target);
42
+ };
43
+ return (_jsxs("section", { className: "tmw-flow-lane", "aria-label": t("tmw.flow.aria"), "data-tmw-lane": "flow", children: [_jsxs("header", { className: "tmw-lane-heading", children: [_jsxs("div", { children: [_jsx("strong", { children: t("tmw.flow.title") }), _jsxs("span", { children: [" \u00B7 ", t("tmw.flow.edge_count", undefined, { count: document.edges.length })] })] }), _jsx("small", { children: t(canRevise ? "tmw.flow.connect_hint" : "tmw.flow.begin_hint") })] }), _jsxs("div", { className: "tmw-flow-lane__palette", role: "toolbar", "aria-label": t("tmw.palette.add_aria"), children: [TMW_NODE_PALETTE.map((item) => (_jsxs("button", { type: "button", disabled: !canRevise, onClick: () => onAddNode(item.kind), children: ["\uFF0B ", t(item.labelKey)] }, item.kind))), _jsx("button", { type: "button", disabled: !canRevise, onClick: () => selectedStableId !== null && onDeleteNode(selectedStableId), children: "\u5220\u9664\u9009\u4E2D\u8282\u70B9" }), _jsx("button", { type: "button", disabled: !canRevise, onClick: persistLayout, children: "\u4FDD\u5B58\u5E03\u5C40" })] }), _jsx("div", { className: "tmw-flow-lane__canvas", "data-testid": "tmw-reactflow-canvas", children: _jsxs(ReactFlow, { nodes: nodes, edges: graph.edges, fitView: true, fitViewOptions: { padding: 0.25 }, nodesDraggable: canRevise, nodesConnectable: canRevise, elementsSelectable: true, onNodeClick: (_, node) => onSelectStableId(node.id), onNodesChange: handleNodesChange, onConnect: handleConnect, onEdgeDoubleClick: (_, edge) => canRevise && onDisconnect(edge.source, edge.target), proOptions: { hideAttribution: true }, children: [_jsx(Background, { variant: BackgroundVariant.Dots, gap: 22, size: 1.4 }), _jsx(Controls, { showInteractive: false }), _jsx(MiniMap, { pannable: true, zoomable: true }), graph.nodes.length === 0 ? (_jsxs("div", { className: "tmw-flow-lane__empty", children: [_jsx("strong", { children: t("tmw.flow.empty_title") }), _jsx("span", { children: t("tmw.flow.empty_help") })] })) : null] }) })] }));
44
+ }
@@ -0,0 +1,17 @@
1
+ import type { TmwDiagnostic, TriggerDocumentV3, TriggerNodeKindV3 } from "@world-engines/tmw";
2
+ import { type TmwTranslate } from "./translate.js";
3
+ export interface TmwSourceLaneProps {
4
+ readonly document: TriggerDocumentV3;
5
+ readonly diagnostics: ReadonlyArray<TmwDiagnostic>;
6
+ readonly selectedStableId: string | null;
7
+ readonly onSelectStableId: (stableId: string) => void;
8
+ readonly canRevise: boolean;
9
+ readonly onAddNode: (kind: TriggerNodeKindV3) => void;
10
+ readonly translate?: TmwTranslate;
11
+ }
12
+ export declare const TMW_NODE_PALETTE: ReadonlyArray<Readonly<{
13
+ kind: TriggerNodeKindV3;
14
+ labelKey: string;
15
+ descriptionKey: string;
16
+ }>>;
17
+ export declare function TmwSourceLane({ document, diagnostics, selectedStableId, onSelectStableId, canRevise, onAddNode, translate, }: TmwSourceLaneProps): JSX.Element;
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { defaultTmwTranslate } from "./translate.js";
3
+ const documentNodes = (document) => [
4
+ ...document.events,
5
+ ...document.conditions,
6
+ ...document.effects,
7
+ ...document.presentations,
8
+ ];
9
+ export const TMW_NODE_PALETTE = [
10
+ {
11
+ kind: "event",
12
+ labelKey: "tmw.palette.event.label",
13
+ descriptionKey: "tmw.palette.event.description",
14
+ },
15
+ {
16
+ kind: "condition",
17
+ labelKey: "tmw.palette.condition.label",
18
+ descriptionKey: "tmw.palette.condition.description",
19
+ },
20
+ {
21
+ kind: "effect",
22
+ labelKey: "tmw.palette.effect.label",
23
+ descriptionKey: "tmw.palette.effect.description",
24
+ },
25
+ {
26
+ kind: "presentation",
27
+ labelKey: "tmw.palette.presentation.label",
28
+ descriptionKey: "tmw.palette.presentation.description",
29
+ },
30
+ ];
31
+ export function TmwSourceLane({ document, diagnostics, selectedStableId, onSelectStableId, canRevise, onAddNode, translate, }) {
32
+ const t = translate ?? defaultTmwTranslate;
33
+ const nodes = documentNodes(document);
34
+ return (_jsxs("section", { className: "tmw-source-lane", "aria-label": t("tmw.source.aria"), "data-tmw-lane": "source", children: [_jsxs("aside", { className: "tmw-source-lane__palette", "aria-label": t("tmw.palette.aria"), children: [_jsxs("div", { className: "tmw-lane-heading", children: [_jsxs("div", { children: [_jsx("strong", { children: t("tmw.palette.title") }), _jsx("span", { children: t("tmw.palette.authority") })] }), !canRevise ? _jsx("small", { children: t("tmw.palette.begin_hint") }) : null] }), _jsx("div", { className: "tmw-node-palette", children: TMW_NODE_PALETTE.map((item) => (_jsxs("button", { type: "button", className: `tmw-node-palette__item tmw-node-palette__item--${item.kind}`, disabled: !canRevise, onClick: () => onAddNode(item.kind), children: [_jsxs("strong", { children: ["\uFF0B ", t(item.labelKey)] }), _jsx("span", { children: t(item.descriptionKey) })] }, item.kind))) })] }), _jsxs("div", { className: "tmw-source-lane__document", children: [_jsx("header", { className: "tmw-lane-heading", children: _jsxs("div", { children: [_jsx("strong", { children: t("tmw.source.title") }), _jsxs("span", { children: [" \u00B7 ", t("tmw.source.node_count", undefined, { count: nodes.length })] })] }) }), diagnostics.filter((diagnostic) => diagnostic.related_stable_ids.length === 0).map((diagnostic) => (_jsxs("div", { className: "tmw-diagnostic", role: "alert", children: [diagnostic.code, " \u00B7 ", diagnostic.path] }, `${diagnostic.path}:${diagnostic.code}`))), nodes.length === 0 ? (_jsxs("div", { className: "tmw-empty-state", children: [_jsx("strong", { children: t("tmw.source.empty_title") }), _jsx("p", { children: t("tmw.source.empty_help") })] })) : (_jsx("ol", { className: "tmw-source-node-list", children: nodes.map((node) => {
35
+ const nodeDiagnostics = diagnostics.filter((diagnostic) => diagnostic.related_stable_ids.includes(node.stable_id));
36
+ return (_jsxs("li", { children: [_jsxs("button", { type: "button", "aria-pressed": selectedStableId === node.stable_id, onClick: () => onSelectStableId(node.stable_id), children: [_jsx("span", { className: `tmw-node-kind tmw-node-kind--${node.kind}`, children: node.kind }), _jsx("code", { children: node.stable_id }), _jsx("span", { children: node.effective_time.domain })] }), nodeDiagnostics.map((diagnostic) => (_jsxs("div", { className: "tmw-diagnostic", role: "alert", children: [diagnostic.code, " \u00B7 ", diagnostic.path] }, `${diagnostic.path}:${diagnostic.code}`)))] }, node.stable_id));
37
+ }) }))] })] }));
38
+ }
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from "react";
2
+ export declare function TmwTabs<T extends string>({ items, value, onSelect, ariaLabel, testId }: {
3
+ readonly items: readonly {
4
+ readonly id: T;
5
+ readonly label: string;
6
+ }[];
7
+ readonly value: T;
8
+ readonly onSelect: (value: T) => void;
9
+ readonly ariaLabel: string;
10
+ readonly testId?: string;
11
+ }): JSX.Element;
12
+ export declare function TmwToggle({ active, onToggle, label, testId }: {
13
+ readonly active: boolean;
14
+ readonly onToggle: () => void;
15
+ readonly label: string;
16
+ readonly testId?: string;
17
+ }): JSX.Element;
18
+ export declare function TmwPanel({ children }: {
19
+ readonly children: ReactNode;
20
+ }): JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ export function TmwTabs({ items, value, onSelect, ariaLabel, testId }) {
3
+ return _jsx("div", { "aria-label": ariaLabel, "data-testid": testId, role: "tablist", children: items.map((item) => _jsx("button", { "aria-selected": value === item.id, onClick: () => onSelect(item.id), role: "tab", type: "button", children: item.label }, item.id)) });
4
+ }
5
+ export function TmwToggle({ active, onToggle, label, testId }) {
6
+ return _jsx("button", { "aria-pressed": active, "data-testid": testId, onClick: onToggle, type: "button", children: label });
7
+ }
8
+ export function TmwPanel({ children }) { return _jsx(_Fragment, { children: children }); }
@@ -0,0 +1,9 @@
1
+ import type { TmwSimulationTraceEntry } from "@world-engines/tmw";
2
+ import { type TmwTranslate } from "./translate.js";
3
+ export interface TmwTraceLaneProps {
4
+ readonly trace: ReadonlyArray<TmwSimulationTraceEntry>;
5
+ readonly selectedStableId: string | null;
6
+ readonly onSelectStableId: (stableId: string) => void;
7
+ readonly translate?: TmwTranslate;
8
+ }
9
+ export declare function TmwTraceLane({ trace, selectedStableId, onSelectStableId, translate, }: TmwTraceLaneProps): JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { defaultTmwTranslate } from "./translate.js";
3
+ export function TmwTraceLane({ trace, selectedStableId, onSelectStableId, translate, }) {
4
+ const t = translate ?? defaultTmwTranslate;
5
+ return (_jsxs("section", { className: "tmw-trace-lane", "aria-label": t("tmw.trace.aria"), "data-tmw-lane": "trace", children: [_jsx("header", { className: "tmw-lane-heading", children: _jsxs("div", { children: [_jsx("strong", { children: t("tmw.trace.title") }), _jsxs("span", { children: [" \u00B7 ", t("tmw.trace.step_count", undefined, { count: trace.length })] })] }) }), trace.length === 0 ? (_jsxs("div", { className: "tmw-empty-state", children: [_jsx("strong", { children: t("tmw.trace.empty") }), _jsx("p", { children: t("tmw.trace.empty_help") })] })) : (_jsx("ol", { className: "tmw-trace-list", children: trace.map((entry) => (_jsx("li", { children: _jsxs("button", { type: "button", "aria-pressed": selectedStableId === entry.stable_id, onClick: () => onSelectStableId(entry.stable_id), children: [entry.step, ". ", entry.kind, " \u00B7 ", entry.stable_id, " \u00B7 ", entry.clock_domain, " \u00B7 ", t("tmw.trace.effective"), " ", entry.effective_value, " \u00B7 ", entry.disposition] }) }, `${entry.step}:${entry.stable_id}`))) }))] }));
6
+ }
@@ -0,0 +1,9 @@
1
+ import { type TmwDiagnostic, type TmwSimulationTraceEntry, type TriggerDocumentV3 } from "@world-engines/tmw";
2
+ export interface TriggerDebugTabProps {
3
+ readonly document?: TriggerDocumentV3;
4
+ readonly diagnostics?: ReadonlyArray<TmwDiagnostic>;
5
+ readonly trace?: ReadonlyArray<TmwSimulationTraceEntry>;
6
+ readonly selectedStableId?: string | null;
7
+ readonly onSelectStableId?: (stableId: string) => void;
8
+ }
9
+ export declare function TriggerDebugTab({ document, diagnostics, trace, selectedStableId, onSelectStableId, }: TriggerDebugTabProps): JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { allTriggerNodesV3, } from "@world-engines/tmw";
4
+ import { TmwTabs } from "./TmwTabs.js";
5
+ const DOMAINS = [
6
+ { id: "logical_seq", label: "逻辑序列" },
7
+ { id: "world_time", label: "世界时间" },
8
+ { id: "presentation_seq", label: "演出序列" },
9
+ { id: "real_time", label: "实时" },
10
+ ];
11
+ export function TriggerDebugTab({ document, diagnostics = [], trace = [], selectedStableId = null, onSelectStableId, }) {
12
+ const [domain, setDomain] = useState("logical_seq");
13
+ if (document === undefined) {
14
+ return _jsx("section", { role: "alert", children: "TMW3 SourceV3 \u5C1A\u672A\u901A\u8FC7\u6253\u5F00\u5C4F\u969C\uFF0C\u8C03\u8BD5\u5668\u5DF2\u963B\u65AD\u3002" });
15
+ }
16
+ const nodes = allTriggerNodesV3(document).filter((node) => node.effective_time.domain === domain);
17
+ return (_jsxs("section", { "aria-label": "TMW3 \u56DB\u65F6\u57DF\u8C03\u8BD5\u5668", "data-tmw-debug-domain": domain, children: [_jsx(TmwTabs, { items: DOMAINS, value: domain, onSelect: setDomain, ariaLabel: "\u56DB\u65F6\u57DF" }), nodes.length === 0 ? (_jsxs("p", { children: [domain, " \u65F6\u57DF\u6CA1\u6709\u8282\u70B9\u3002"] })) : (_jsx("ol", { children: nodes.map((node) => {
18
+ const nodeTrace = trace.filter((entry) => entry.stable_id === node.stable_id);
19
+ const nodeDiagnostics = diagnostics.filter((item) => item.related_stable_ids.includes(node.stable_id));
20
+ const effectiveTime = node.effective_time;
21
+ return (_jsxs("li", { children: [_jsxs("button", { type: "button", "aria-pressed": selectedStableId === node.stable_id, onClick: () => onSelectStableId?.(node.stable_id), children: [node.kind, " \u00B7 ", node.stable_id, " \u00B7 \u5230\u671F ", effectiveTime.due_value, " \u00B7 \u751F\u6548", " ", effectiveTime.effective_value, " \u00B7 ", effectiveTime.crossing_policy, effectiveTime.domain === "real_time"
22
+ ? ` · ${effectiveTime.real_time_clock} · ${effectiveTime.wall_clock_policy}`
23
+ : null] }), _jsxs("span", { children: [" \u00B7 trace ", nodeTrace.length] }), nodeDiagnostics.map((item) => (_jsxs("div", { role: "alert", children: [item.code, " \u00B7 ", item.path] }, `${item.path}:${item.code}`)))] }, node.stable_id));
24
+ }) })), _jsx("p", { children: "\u8C03\u8BD5\u5668\u53EA\u5C55\u793A P1 \u7EAF\u6A21\u62DF\u5668 trace\uFF0C\u4E0D\u6267\u884C\u751F\u4EA7 effect\u3002" })] }));
25
+ }
@@ -0,0 +1,3 @@
1
+ export type TmwTranslate = (key: string, fallback?: string, values?: Readonly<Record<string, unknown>>) => string;
2
+ /** 未注入宿主 i18n 时保留 key,绝不猜测文案。 */
3
+ export declare const defaultTmwTranslate: TmwTranslate;
@@ -0,0 +1,2 @@
1
+ /** 未注入宿主 i18n 时保留 key,绝不猜测文案。 */
2
+ export const defaultTmwTranslate = (key, fallback) => fallback ?? key;
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from "react";
2
+ export interface ViewFile {
3
+ readonly path: string;
4
+ readonly content: string;
5
+ readonly readonly: boolean;
6
+ }
7
+ export interface ViewWorkspaceProps {
8
+ readonly files: readonly ViewFile[];
9
+ readonly selected_path: string | null;
10
+ readonly disabled: boolean;
11
+ readonly onSelectFile: (path: string) => Promise<void>;
12
+ readonly onSaveFile: (path: string, content: string) => Promise<void>;
13
+ readonly onPreview: () => Promise<void>;
14
+ readonly renderMonaco?: (props: ViewWorkspaceProps & {
15
+ readonly file: ViewFile | null;
16
+ }) => ReactNode;
17
+ readonly renderPreview?: () => ReactNode;
18
+ }
19
+ export declare function ViewWorkspace(props: ViewWorkspaceProps): JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ export function ViewWorkspace(props) {
4
+ const file = props.files.find((item) => item.path === props.selected_path) ?? null;
5
+ const [content, setContent] = useState(file?.content ?? "");
6
+ useEffect(() => setContent(file?.content ?? ""), [file]);
7
+ return (_jsxs("section", { "aria-label": "View \u5DE5\u4F5C\u533A", "data-testid": "view-workspace", children: [_jsxs("nav", { "aria-label": "View \u6587\u4EF6", children: [props.files.length === 0 ? _jsx("p", { children: "\u5F53\u524D View \u6CA1\u6709\u53EF\u7F16\u8F91\u6587\u4EF6\u3002" }) : null, props.files.map((item) => (_jsx("button", { "aria-current": item.path === props.selected_path ? "page" : undefined, disabled: props.disabled, onClick: () => void props.onSelectFile(item.path), type: "button", children: item.path }, item.path)))] }), _jsx("div", { "aria-label": "Monaco \u7F16\u8F91\u5668", children: props.renderMonaco?.({ ...props, file }) ?? (file === null ? _jsx("p", { children: "\u9009\u62E9\u4E00\u4E2A View \u6587\u4EF6\u4EE5\u5F00\u59CB\u624B\u52A8\u7F16\u8F91\u3002" }) : (_jsxs(_Fragment, { children: [_jsx("textarea", { "aria-label": "View \u6E90\u7801", disabled: props.disabled || file.readonly, onChange: (event) => setContent(event.target.value), readOnly: file.readonly, value: content }), _jsx("button", { disabled: props.disabled || file.readonly || content === file.content, onClick: () => void props.onSaveFile(file.path, content), type: "button", children: "\u4FDD\u5B58\u6587\u4EF6" })] }))) }), _jsxs("div", { "aria-label": "View \u9884\u89C8", "data-testid": "view-preview-workspace", children: [_jsx("button", { disabled: props.disabled, onClick: () => void props.onPreview(), type: "button", children: "\u5237\u65B0\u9884\u89C8" }), props.renderPreview?.()] })] }));
8
+ }