oh-my-knowledge 0.51.2 → 0.52.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/README.md +27 -6
- package/README.zh.md +27 -6
- package/dist/assets/agent-skills/omk/SKILL.md +8 -8
- package/dist/observability/codex-conversation-index.d.ts +88 -0
- package/dist/observability/codex-conversation-index.js +566 -0
- package/dist/observability/codex-protocol.d.ts +12 -0
- package/dist/observability/codex-protocol.js +78 -0
- package/dist/observability/codex-tool-status.d.ts +16 -0
- package/dist/observability/codex-tool-status.js +114 -0
- package/dist/observability/codex-trace-adapter.js +342 -119
- package/dist/observability/conversation-catalog.d.ts +38 -0
- package/dist/observability/conversation-catalog.js +573 -0
- package/dist/observability/conversation-index-process.d.ts +1 -0
- package/dist/observability/conversation-index-process.js +31 -0
- package/dist/observability/conversation-view-model.d.ts +5 -0
- package/dist/observability/conversation-view-model.js +100 -0
- package/dist/observability/experience.d.ts +3 -1
- package/dist/observability/experience.js +248 -7
- package/dist/observability/inbox.js +40 -2
- package/dist/observability/knowledge-debugger.d.ts +4 -0
- package/dist/observability/knowledge-debugger.js +364 -0
- package/dist/observability/polling-subscription-hub.d.ts +27 -0
- package/dist/observability/polling-subscription-hub.js +149 -0
- package/dist/observability/source-record-archive.d.ts +13 -0
- package/dist/observability/source-record-archive.js +324 -0
- package/dist/observability/task-window.d.ts +8 -0
- package/dist/observability/task-window.js +36 -0
- package/dist/observability/trace-ir.d.ts +66 -1
- package/dist/observability/trace-source.d.ts +1 -0
- package/dist/observability/trace-source.js +12 -5
- package/dist/observability/turn-index.d.ts +3 -0
- package/dist/observability/turn-index.js +251 -0
- package/dist/renderer/conversation-renderer.d.ts +9 -0
- package/dist/renderer/conversation-renderer.js +410 -0
- package/dist/renderer/icons.js +1 -0
- package/dist/renderer/inline-markdown.d.ts +6 -0
- package/dist/renderer/inline-markdown.js +158 -0
- package/dist/renderer/knowledge-debugger-renderer.d.ts +9 -0
- package/dist/renderer/knowledge-debugger-renderer.js +2280 -0
- package/dist/renderer/observation-inbox-renderer.js +9 -0
- package/dist/renderer/skill-list-renderer.js +2 -1
- package/dist/renderer/trajectory-live.d.ts +42 -0
- package/dist/renderer/trajectory-live.js +258 -0
- package/dist/renderer/trajectory-routing.d.ts +60 -0
- package/dist/renderer/trajectory-routing.js +351 -0
- package/dist/server/report-server.d.ts +4 -1
- package/dist/server/report-server.js +274 -6
- package/dist/types/observability.d.ts +225 -1
- package/package.json +8 -5
|
@@ -1886,10 +1886,13 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
1886
1886
|
user_message: '用户消息',
|
|
1887
1887
|
synthetic_user_event: '构造事件',
|
|
1888
1888
|
assistant_message: '助手回复',
|
|
1889
|
+
model_activity: '模型活动',
|
|
1890
|
+
agent_activity: '协作代理活动',
|
|
1889
1891
|
tool_use: '工具调用',
|
|
1890
1892
|
tool_result: '工具结果',
|
|
1891
1893
|
skill_context: '能力说明',
|
|
1892
1894
|
runtime_context: '运行注入',
|
|
1895
|
+
lifecycle: '运行状态',
|
|
1893
1896
|
observation: '过程发现',
|
|
1894
1897
|
};
|
|
1895
1898
|
return labels[kind] ?? kind;
|
|
@@ -2393,6 +2396,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2393
2396
|
return { icon: 'S', label: 'Skill 注入上下文', tone: 'skill' };
|
|
2394
2397
|
if (event.kind === 'runtime_context')
|
|
2395
2398
|
return { icon: 'R', label: event.label === 'command envelope' ? '命令注入上下文' : '运行时注入上下文', tone: 'skill' };
|
|
2399
|
+
if (event.kind === 'lifecycle')
|
|
2400
|
+
return { icon: 'L', label: '运行状态', tone: 'runtime' };
|
|
2396
2401
|
return { icon: 'O', label: '过程发现', tone: 'observation' };
|
|
2397
2402
|
};
|
|
2398
2403
|
const isSkillLaunchResult = (event) => event.kind === 'tool_result' && /^Launching skill:/i.test((event.fullText ?? event.snippet ?? '').trim());
|
|
@@ -2629,6 +2634,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2629
2634
|
if (event.kind === 'runtime_context') {
|
|
2630
2635
|
badges.push({ label: '不计入用户交互', className: 'metric-skill-context', title: event.label === 'command envelope' ? '这是斜杠命令的系统包装内容,不是人工用户原话,不计入用户消息、追问、纠正、情绪反馈或用户硬性要求。' : '这是 SDK/工作台注入的运行时上下文,不是人工用户原话,不计入用户消息、追问、纠正、情绪反馈或用户硬性要求。' });
|
|
2631
2636
|
}
|
|
2637
|
+
if (event.kind === 'lifecycle') {
|
|
2638
|
+
badges.push({ label: '运行状态', className: 'metric-neutral', title: '这是 runtime 记录的生命周期状态,不是模型上下文,也不计入用户交互。' });
|
|
2639
|
+
}
|
|
2632
2640
|
if (event.kind === 'observation') {
|
|
2633
2641
|
badges.push({ label: '过程发现来源', className: 'metric-explicit', title: '这条事件来自 observation 信号,会进入过程发现类指标。' });
|
|
2634
2642
|
}
|
|
@@ -5157,6 +5165,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
5157
5165
|
<span>入口 ${e(inboxEntrypointShort(session))}</span>
|
|
5158
5166
|
<span>调用段 ${session.invocationIds.length}</span>
|
|
5159
5167
|
<span>工具调用 ${indicators.toolCallCount}</span>
|
|
5168
|
+
<a href="/conversations/${encodeURIComponent(session.threadId)}${lang === DEFAULT_LANG ? '' : `?lang=${lang}`}" onclick="event.stopPropagation()">${lang === 'zh' ? '查看对话任务' : 'Conversation tasks'}</a>
|
|
5160
5169
|
</div>
|
|
5161
5170
|
${navHtml}
|
|
5162
5171
|
<template id="${flowTemplateId}">${inboxRenderSessionFlow(cardSkillName, session, siblings, flowSummaryText)}</template>
|
|
@@ -90,7 +90,8 @@ function renderPanel(a, lang) {
|
|
|
90
90
|
<h1>${zh ? 'Skill 健康评测工作台' : 'Skill Health Dashboard'}</h1>
|
|
91
91
|
<span class="phead-sub">${a.totalSkills} ${zh ? '个 skill' : 'skills'}</span>
|
|
92
92
|
<span class="phead-when">${icon('clock', { size: 13 })} ${zh ? '更新于' : 'updated'} ${fmtDate(a.lastTs)}</span>
|
|
93
|
-
<a href="
|
|
93
|
+
<a href="/${zh ? '' : '?lang=en'}" style="margin-left:auto;font-size:13px;color:var(--accent);text-decoration:none;white-space:nowrap">${zh ? '对话总览' : 'Conversation overview'} →</a>
|
|
94
|
+
<a href="/managed${zh ? '' : '?lang=en'}" style="font-size:13px;color:var(--accent);text-decoration:none;white-space:nowrap">${zh ? '受管决策史' : 'Managed history'} →</a>
|
|
94
95
|
</div>`;
|
|
95
96
|
// 综合健康(细线总览首格):ring + 标签 + 说明入口。
|
|
96
97
|
const ovMain = `<div class="ov-cell ov-main">
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface TrajectoryLiveLabels {
|
|
2
|
+
connecting: string;
|
|
3
|
+
live: string;
|
|
4
|
+
syncing: string;
|
|
5
|
+
reconnecting: string;
|
|
6
|
+
failed: string;
|
|
7
|
+
following: string;
|
|
8
|
+
resume: string;
|
|
9
|
+
pending: string;
|
|
10
|
+
completed: string;
|
|
11
|
+
aborted: string;
|
|
12
|
+
interrupted: string;
|
|
13
|
+
unknown: string;
|
|
14
|
+
pauseTitle: string;
|
|
15
|
+
}
|
|
16
|
+
export interface TrajectoryLiveClientOptions {
|
|
17
|
+
shell: HTMLElement;
|
|
18
|
+
timeline: HTMLElement | null;
|
|
19
|
+
liveState: HTMLElement | null;
|
|
20
|
+
followButton: HTMLButtonElement | null;
|
|
21
|
+
followLabel: HTMLElement | null;
|
|
22
|
+
liveEndpoint: string;
|
|
23
|
+
labels: TrajectoryLiveLabels;
|
|
24
|
+
getMode: () => string;
|
|
25
|
+
setMode: (mode: string) => void;
|
|
26
|
+
isScrollTrackingSuppressed?: () => boolean;
|
|
27
|
+
refreshSnapshot?: (state: TrajectoryLiveViewState) => Promise<void>;
|
|
28
|
+
browserWindow: Window;
|
|
29
|
+
browserDocument: Document;
|
|
30
|
+
}
|
|
31
|
+
export interface TrajectoryLiveViewState {
|
|
32
|
+
mode: string;
|
|
33
|
+
scrollLeft: number;
|
|
34
|
+
followLatest: boolean;
|
|
35
|
+
animateToLatest?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface TrajectoryLiveController {
|
|
38
|
+
pauseFollowing: () => void;
|
|
39
|
+
dispose: () => void;
|
|
40
|
+
}
|
|
41
|
+
export declare function createTrajectoryLiveController(options: TrajectoryLiveClientOptions): TrajectoryLiveController;
|
|
42
|
+
export declare function renderTrajectoryLiveClientSource(): string;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
export function createTrajectoryLiveController(options) {
|
|
2
|
+
const { shell, timeline, liveState, followButton, followLabel, liveEndpoint, labels, browserWindow, browserDocument, } = options;
|
|
3
|
+
const stateKey = `omk:live-trajectory:${browserWindow.location.pathname}`;
|
|
4
|
+
const lifecycle = new AbortController();
|
|
5
|
+
let followLatest = Boolean(liveEndpoint);
|
|
6
|
+
let pendingRevision = '';
|
|
7
|
+
let terminalState;
|
|
8
|
+
let refreshTimer;
|
|
9
|
+
let scrollReleaseTimer;
|
|
10
|
+
let suppressScrollTracking = false;
|
|
11
|
+
let liveSource;
|
|
12
|
+
const setConnectionState = (state, label) => {
|
|
13
|
+
if (!liveState)
|
|
14
|
+
return;
|
|
15
|
+
liveState.dataset.state = state;
|
|
16
|
+
liveState.textContent = label;
|
|
17
|
+
};
|
|
18
|
+
const isNearLatest = () => {
|
|
19
|
+
if (!timeline)
|
|
20
|
+
return true;
|
|
21
|
+
const remaining = timeline.scrollWidth - timeline.clientWidth - timeline.scrollLeft;
|
|
22
|
+
return remaining <= 72;
|
|
23
|
+
};
|
|
24
|
+
const scrollToLatest = (smooth = false) => {
|
|
25
|
+
if (!timeline || options.getMode() !== 'semantic')
|
|
26
|
+
return;
|
|
27
|
+
suppressScrollTracking = true;
|
|
28
|
+
const left = Math.max(0, timeline.scrollWidth - timeline.clientWidth);
|
|
29
|
+
const reduceMotion = browserWindow.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
|
|
30
|
+
if (smooth && !reduceMotion && typeof timeline.scrollTo === 'function') {
|
|
31
|
+
timeline.scrollTo({ left, behavior: 'smooth' });
|
|
32
|
+
if (scrollReleaseTimer !== undefined)
|
|
33
|
+
browserWindow.clearTimeout(scrollReleaseTimer);
|
|
34
|
+
scrollReleaseTimer = browserWindow.setTimeout(() => {
|
|
35
|
+
timeline.scrollLeft = left;
|
|
36
|
+
suppressScrollTracking = false;
|
|
37
|
+
scrollReleaseTimer = undefined;
|
|
38
|
+
}, 700);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
timeline.scrollLeft = left;
|
|
42
|
+
browserWindow.requestAnimationFrame(() => { suppressScrollTracking = false; });
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const updateFollowControl = () => {
|
|
46
|
+
if (!followButton || !followLabel)
|
|
47
|
+
return;
|
|
48
|
+
const state = terminalState ?? (!followLatest && pendingRevision
|
|
49
|
+
? 'pending'
|
|
50
|
+
: (followLatest ? 'following' : 'paused'));
|
|
51
|
+
const terminalLabel = terminalState ? labels[terminalState] : undefined;
|
|
52
|
+
followButton.dataset.state = state;
|
|
53
|
+
followButton.dataset.following = String(followLatest);
|
|
54
|
+
followButton.setAttribute('aria-pressed', String(followLatest));
|
|
55
|
+
followLabel.textContent = terminalLabel ?? (state === 'pending'
|
|
56
|
+
? labels.pending
|
|
57
|
+
: state === 'following'
|
|
58
|
+
? labels.following
|
|
59
|
+
: labels.resume);
|
|
60
|
+
followButton.title = state === 'following' ? labels.pauseTitle : followLabel.textContent;
|
|
61
|
+
};
|
|
62
|
+
const setFollowing = (next, shouldScroll = next) => {
|
|
63
|
+
if (!next && refreshTimer !== undefined) {
|
|
64
|
+
browserWindow.clearTimeout(refreshTimer);
|
|
65
|
+
refreshTimer = undefined;
|
|
66
|
+
setConnectionState('live', labels.live);
|
|
67
|
+
}
|
|
68
|
+
followLatest = next;
|
|
69
|
+
updateFollowControl();
|
|
70
|
+
if (shouldScroll)
|
|
71
|
+
scrollToLatest(true);
|
|
72
|
+
};
|
|
73
|
+
const readViewState = () => {
|
|
74
|
+
try {
|
|
75
|
+
const raw = browserWindow.sessionStorage.getItem(stateKey);
|
|
76
|
+
browserWindow.sessionStorage.removeItem(stateKey);
|
|
77
|
+
return raw ? JSON.parse(raw) : undefined;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const currentViewState = (animateToLatest = false) => ({
|
|
84
|
+
mode: options.getMode(),
|
|
85
|
+
scrollLeft: timeline?.scrollLeft ?? 0,
|
|
86
|
+
followLatest,
|
|
87
|
+
...(animateToLatest ? { animateToLatest: true } : {}),
|
|
88
|
+
});
|
|
89
|
+
const saveViewState = (state = currentViewState()) => {
|
|
90
|
+
try {
|
|
91
|
+
browserWindow.sessionStorage.setItem(stateKey, JSON.stringify(state));
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Storage can be unavailable in hardened browsers.
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const restoreViewState = () => {
|
|
98
|
+
const state = readViewState();
|
|
99
|
+
if (state?.mode && ['semantic', 'normalized', 'source'].includes(state.mode))
|
|
100
|
+
options.setMode(state.mode);
|
|
101
|
+
if (typeof state?.followLatest === 'boolean')
|
|
102
|
+
followLatest = state.followLatest;
|
|
103
|
+
if (!liveEndpoint || options.getMode() !== 'semantic')
|
|
104
|
+
followLatest = false;
|
|
105
|
+
updateFollowControl();
|
|
106
|
+
if (timeline && Number.isFinite(state?.scrollLeft)) {
|
|
107
|
+
// Browsers can dispatch this assignment's scroll event after listeners are attached below.
|
|
108
|
+
// Keep restoration programmatic until the final frame settles so it cannot pause live follow.
|
|
109
|
+
suppressScrollTracking = true;
|
|
110
|
+
timeline.scrollLeft = Math.min(state?.scrollLeft ?? 0, Math.max(0, timeline.scrollWidth - timeline.clientWidth));
|
|
111
|
+
}
|
|
112
|
+
browserWindow.requestAnimationFrame(() => {
|
|
113
|
+
browserWindow.requestAnimationFrame(() => {
|
|
114
|
+
if (followLatest && liveEndpoint)
|
|
115
|
+
scrollToLatest(Boolean(state?.animateToLatest));
|
|
116
|
+
else if (timeline && Number.isFinite(state?.scrollLeft)) {
|
|
117
|
+
timeline.scrollLeft = state?.scrollLeft ?? 0;
|
|
118
|
+
browserWindow.requestAnimationFrame(() => { suppressScrollTracking = false; });
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
suppressScrollTracking = false;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
const reloadNow = async () => {
|
|
127
|
+
if (!pendingRevision)
|
|
128
|
+
return;
|
|
129
|
+
if (refreshTimer !== undefined)
|
|
130
|
+
browserWindow.clearTimeout(refreshTimer);
|
|
131
|
+
if (scrollReleaseTimer !== undefined)
|
|
132
|
+
browserWindow.clearTimeout(scrollReleaseTimer);
|
|
133
|
+
refreshTimer = undefined;
|
|
134
|
+
scrollReleaseTimer = undefined;
|
|
135
|
+
setConnectionState('syncing', labels.syncing);
|
|
136
|
+
// Live snapshots can arrive faster than native smooth scrolling settles.
|
|
137
|
+
// Restore them at the latest edge immediately so following never lags behind.
|
|
138
|
+
const viewState = currentViewState();
|
|
139
|
+
saveViewState(viewState);
|
|
140
|
+
if (options.refreshSnapshot) {
|
|
141
|
+
const revision = pendingRevision;
|
|
142
|
+
pendingRevision = '';
|
|
143
|
+
updateFollowControl();
|
|
144
|
+
try {
|
|
145
|
+
await options.refreshSnapshot(viewState);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
pendingRevision = revision;
|
|
149
|
+
setConnectionState('reconnecting', labels.reconnecting);
|
|
150
|
+
updateFollowControl();
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
browserWindow.location.reload();
|
|
155
|
+
};
|
|
156
|
+
const applyPendingUpdate = () => {
|
|
157
|
+
updateFollowControl();
|
|
158
|
+
if (!pendingRevision
|
|
159
|
+
|| !followLatest
|
|
160
|
+
|| options.getMode() !== 'semantic'
|
|
161
|
+
|| browserDocument.hidden
|
|
162
|
+
|| refreshTimer !== undefined)
|
|
163
|
+
return;
|
|
164
|
+
setConnectionState('syncing', labels.syncing);
|
|
165
|
+
refreshTimer = browserWindow.setTimeout(() => { void reloadNow(); }, 600);
|
|
166
|
+
};
|
|
167
|
+
const pauseFollowing = () => {
|
|
168
|
+
if (!liveEndpoint || !followLatest)
|
|
169
|
+
return;
|
|
170
|
+
setFollowing(false, false);
|
|
171
|
+
};
|
|
172
|
+
const dispose = () => {
|
|
173
|
+
lifecycle.abort();
|
|
174
|
+
liveSource?.close();
|
|
175
|
+
if (refreshTimer !== undefined)
|
|
176
|
+
browserWindow.clearTimeout(refreshTimer);
|
|
177
|
+
if (scrollReleaseTimer !== undefined)
|
|
178
|
+
browserWindow.clearTimeout(scrollReleaseTimer);
|
|
179
|
+
refreshTimer = undefined;
|
|
180
|
+
scrollReleaseTimer = undefined;
|
|
181
|
+
};
|
|
182
|
+
restoreViewState();
|
|
183
|
+
timeline?.addEventListener('scroll', () => {
|
|
184
|
+
if (suppressScrollTracking
|
|
185
|
+
|| options.isScrollTrackingSuppressed?.()
|
|
186
|
+
|| !followLatest
|
|
187
|
+
|| options.getMode() !== 'semantic')
|
|
188
|
+
return;
|
|
189
|
+
if (!isNearLatest())
|
|
190
|
+
pauseFollowing();
|
|
191
|
+
}, { passive: true, signal: lifecycle.signal });
|
|
192
|
+
followButton?.addEventListener('click', () => {
|
|
193
|
+
if (pendingRevision && !followLatest) {
|
|
194
|
+
setFollowing(true, false);
|
|
195
|
+
void reloadNow();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
setFollowing(!followLatest);
|
|
199
|
+
}, { signal: lifecycle.signal });
|
|
200
|
+
browserDocument.addEventListener('visibilitychange', () => {
|
|
201
|
+
if (!browserDocument.hidden)
|
|
202
|
+
applyPendingUpdate();
|
|
203
|
+
}, { signal: lifecycle.signal });
|
|
204
|
+
browserWindow.addEventListener('pagehide', dispose, { once: true, signal: lifecycle.signal });
|
|
205
|
+
const EventSourceConstructor = browserWindow.EventSource;
|
|
206
|
+
if (liveEndpoint && EventSourceConstructor) {
|
|
207
|
+
setConnectionState('connecting', labels.connecting);
|
|
208
|
+
const source = new EventSourceConstructor(liveEndpoint);
|
|
209
|
+
liveSource = source;
|
|
210
|
+
source.addEventListener('open', () => setConnectionState('live', labels.live), { signal: lifecycle.signal });
|
|
211
|
+
source.addEventListener('trajectory', (event) => {
|
|
212
|
+
try {
|
|
213
|
+
const update = JSON.parse(event.data);
|
|
214
|
+
const explicitTerminal = update.status === 'completed'
|
|
215
|
+
|| update.status === 'aborted'
|
|
216
|
+
|| update.status === 'interrupted';
|
|
217
|
+
const streamTerminal = update.liveObservable === false || explicitTerminal;
|
|
218
|
+
terminalState = explicitTerminal
|
|
219
|
+
? update.status
|
|
220
|
+
: update.liveObservable === false ? 'unknown' : undefined;
|
|
221
|
+
if (streamTerminal) {
|
|
222
|
+
source.close();
|
|
223
|
+
if (liveSource === source)
|
|
224
|
+
liveSource = undefined;
|
|
225
|
+
}
|
|
226
|
+
if (!update.revision || update.revision === shell.dataset.liveRevision) {
|
|
227
|
+
const quiet = update.status === 'unknown';
|
|
228
|
+
const connectionLabel = terminalState
|
|
229
|
+
? labels[terminalState]
|
|
230
|
+
: quiet ? labels.reconnecting : labels.live;
|
|
231
|
+
setConnectionState(terminalState ?? (quiet ? 'reconnecting' : 'live'), connectionLabel);
|
|
232
|
+
updateFollowControl();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
pendingRevision = update.revision;
|
|
236
|
+
applyPendingUpdate();
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// Ignore malformed live events and wait for the next revision.
|
|
240
|
+
}
|
|
241
|
+
}, { signal: lifecycle.signal });
|
|
242
|
+
source.addEventListener('trajectory-error', () => {
|
|
243
|
+
source.close();
|
|
244
|
+
if (liveSource === source)
|
|
245
|
+
liveSource = undefined;
|
|
246
|
+
setConnectionState('failed', labels.failed);
|
|
247
|
+
}, { signal: lifecycle.signal });
|
|
248
|
+
source.addEventListener('error', () => {
|
|
249
|
+
if (!pendingRevision)
|
|
250
|
+
setConnectionState('reconnecting', labels.reconnecting);
|
|
251
|
+
}, { signal: lifecycle.signal });
|
|
252
|
+
}
|
|
253
|
+
return { pauseFollowing, dispose };
|
|
254
|
+
}
|
|
255
|
+
export function renderTrajectoryLiveClientSource() {
|
|
256
|
+
// Studio 页面没有独立客户端打包链路;序列化已类型检查的控制器,避免维护第二份浏览器实现。
|
|
257
|
+
return createTrajectoryLiveController.toString();
|
|
258
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export interface RoutingPoint {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
}
|
|
5
|
+
export interface RoutingRect<Owner = unknown> {
|
|
6
|
+
left: number;
|
|
7
|
+
right: number;
|
|
8
|
+
top: number;
|
|
9
|
+
bottom: number;
|
|
10
|
+
owner?: Owner;
|
|
11
|
+
}
|
|
12
|
+
export interface HorizontalObstacleIndex<Owner = unknown> {
|
|
13
|
+
bucketSize: number;
|
|
14
|
+
buckets: Map<number, RoutingRect<Owner>[]>;
|
|
15
|
+
all: RoutingRect<Owner>[];
|
|
16
|
+
}
|
|
17
|
+
export interface FlowRouteMetrics {
|
|
18
|
+
obstacleQueries: number;
|
|
19
|
+
obstacleCandidates: number;
|
|
20
|
+
collisionChecks: number;
|
|
21
|
+
curveCandidates: number;
|
|
22
|
+
corridorCandidates: number;
|
|
23
|
+
}
|
|
24
|
+
export interface FlowRoutePlan {
|
|
25
|
+
routeKind: 'straight' | 'quadratic' | 'corridor';
|
|
26
|
+
d: string;
|
|
27
|
+
from: RoutingPoint;
|
|
28
|
+
to: RoutingPoint;
|
|
29
|
+
arrowFrom: RoutingPoint;
|
|
30
|
+
metrics: FlowRouteMetrics;
|
|
31
|
+
}
|
|
32
|
+
export interface FlowRouteRequest<Owner = unknown> {
|
|
33
|
+
fromRect: RoutingRect<Owner>;
|
|
34
|
+
toRect: RoutingRect<Owner>;
|
|
35
|
+
fromLane: string;
|
|
36
|
+
toLane: string;
|
|
37
|
+
obstacleIndex: HorizontalObstacleIndex<Owner>;
|
|
38
|
+
fromOwner?: Owner;
|
|
39
|
+
toOwner?: Owner;
|
|
40
|
+
}
|
|
41
|
+
export declare function planFlowMarkerProgresses(pathLength: number, markerCount: number): number[];
|
|
42
|
+
export declare function createHorizontalObstacleIndex<Owner>(rects: RoutingRect<Owner>[], bucketSize?: number): HorizontalObstacleIndex<Owner>;
|
|
43
|
+
export declare function queryHorizontalObstacleIndex<Owner>(index: HorizontalObstacleIndex<Owner>, minX: number, maxX: number, minY?: number, maxY?: number): RoutingRect<Owner>[];
|
|
44
|
+
export declare function routingPointInsideRect(point: RoutingPoint, rect: RoutingRect): boolean;
|
|
45
|
+
export declare function routingLineHitsRect(fromPoint: RoutingPoint, toPoint: RoutingPoint, rect: RoutingRect): boolean;
|
|
46
|
+
export declare function routingQuadraticHitsRect(fromPoint: RoutingPoint, control: RoutingPoint, toPoint: RoutingPoint, rect: RoutingRect): boolean;
|
|
47
|
+
export declare function routingMoveToward(fromPoint: RoutingPoint, toPoint: RoutingPoint, distance: number): RoutingPoint;
|
|
48
|
+
export declare function routingEdgePoint(rect: RoutingRect, targetX: number, targetY: number): RoutingPoint;
|
|
49
|
+
export declare function routingConnectionPoints(fromRect: RoutingRect, toRect: RoutingRect): {
|
|
50
|
+
from: RoutingPoint;
|
|
51
|
+
to: RoutingPoint;
|
|
52
|
+
};
|
|
53
|
+
export declare function routingRectPort(rect: RoutingRect, side: 'left' | 'right' | 'top' | 'bottom'): RoutingPoint;
|
|
54
|
+
export declare function routingFlowControlPoint(points: {
|
|
55
|
+
from: RoutingPoint;
|
|
56
|
+
to: RoutingPoint;
|
|
57
|
+
}, requestedBend?: number, side?: number): RoutingPoint;
|
|
58
|
+
export declare function routingRoundedPolylinePath(routePoints: RoutingPoint[], radius?: number): string;
|
|
59
|
+
export declare function planFlowRoute<Owner>(request: FlowRouteRequest<Owner>): FlowRoutePlan | undefined;
|
|
60
|
+
export declare function renderTrajectoryRoutingClientSource(): string;
|