dsh-mega-chat-nav 0.1.1
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 +201 -0
- package/README.en.md +149 -0
- package/README.md +149 -0
- package/cordis.patch.yml +4 -0
- package/lib/client/NavOverlay.js +35 -0
- package/lib/client/NavRail.js +12 -0
- package/lib/client/QuestionNavSettingsTab.js +26 -0
- package/lib/client/QuestionNavStrip.js +555 -0
- package/lib/client/components/ConfigPanel.js +14 -0
- package/lib/client/components/DotRail.js +62 -0
- package/lib/client/components/NavOverlay.js +121 -0
- package/lib/client/components/NavRail.js +97 -0
- package/lib/client/components/RailDispatch.js +22 -0
- package/lib/client/components/RailHost.js +79 -0
- package/lib/client/components/SearchBox.js +66 -0
- package/lib/client/components/SettingsPage.js +46 -0
- package/lib/client/components/StandaloneSettings.js +16 -0
- package/lib/client/components/codex/CodexRail.js +139 -0
- package/lib/client/components/deepseek/DeepseekRail.js +264 -0
- package/lib/client/components/minimal/MinimalRail.js +135 -0
- package/lib/client/components/minimal/cards.js +32 -0
- package/lib/client/components/minimal/dot.js +11 -0
- package/lib/client/components/minimal/hint.js +30 -0
- package/lib/client/components/minimal/paging.js +5 -0
- package/lib/client/components/minimal/quick-settings.js +28 -0
- package/lib/client/components/minimal/search.js +67 -0
- package/lib/client/components/minimal/useRail.js +716 -0
- package/lib/client/components/rail/cards.js +32 -0
- package/lib/client/components/rail/hint.js +30 -0
- package/lib/client/components/rail/paging.js +5 -0
- package/lib/client/components/rail/quick-settings.js +28 -0
- package/lib/client/components/rail/search.js +76 -0
- package/lib/client/components/rail/useRail.js +845 -0
- package/lib/client/components/settings/SettingsPage.js +46 -0
- package/lib/client/components/settings/SettingsTab.js +17 -0
- package/lib/client/components/settings/StandaloneSettings.js +16 -0
- package/lib/client/components/shared/Cascade.js +26 -0
- package/lib/client/components/shared/ConfigPanel.js +27 -0
- package/lib/client/components/shared/Dot.js +8 -0
- package/lib/client/components/shared/Hint.js +5 -0
- package/lib/client/components/shared/PagingButton.js +5 -0
- package/lib/client/components/shared/RailShell.js +78 -0
- package/lib/client/components/shared/SearchBox.js +63 -0
- package/lib/client/components/shared/hooks.js +427 -0
- package/lib/client/components/shared/types.js +1 -0
- package/lib/client/config.js +1 -0
- package/lib/client/engines/jump.js +34 -0
- package/lib/client/engines/useAtBottom.js +25 -0
- package/lib/client/engines/useJump.js +40 -0
- package/lib/client/engines/useScrollSpy.js +88 -0
- package/lib/client/hooks.js +11 -0
- package/lib/client/index.js +312 -0
- package/lib/client/jump.js +255 -0
- package/lib/client/locales.js +142 -0
- package/lib/client/pure.js +61 -0
- package/lib/client/settings.js +369 -0
- package/lib/client/virtual/VirtualList.js +21 -0
- package/lib/client.js +3213 -0
- package/lib/compat.js +145 -0
- package/lib/config.js +37 -0
- package/lib/core/active-dot.js +17 -0
- package/lib/core/align.js +9 -0
- package/lib/core/config.js +37 -0
- package/lib/core/focus.js +38 -0
- package/lib/core/jump.js +98 -0
- package/lib/core/logic/active-dot.js +16 -0
- package/lib/core/logic/focus.js +34 -0
- package/lib/core/logic/jump.js +188 -0
- package/lib/core/logic/nodes.js +59 -0
- package/lib/core/model/nav-item.js +1 -0
- package/lib/core/model/turns.js +73 -0
- package/lib/core/nav-item.js +1 -0
- package/lib/core/nodes.js +57 -0
- package/lib/core/page-size.js +7 -0
- package/lib/core/question-entry.js +1 -0
- package/lib/core/time.js +23 -0
- package/lib/core/turn-dots.js +85 -0
- package/lib/core/utils/measure.js +24 -0
- package/lib/core/utils/time.js +26 -0
- package/lib/index.js +64 -0
- package/lib/nav-item.js +1 -0
- package/lib/nodes.js +59 -0
- package/lib/projection.js +126 -0
- package/lib/schema.js +26 -0
- package/lib/search.js +285 -0
- package/lib/settings.js +89 -0
- package/lib/types/client/NavOverlay.d.ts +18 -0
- package/lib/types/client/NavRail.d.ts +7 -0
- package/lib/types/client/QuestionNavSettingsTab.d.ts +16 -0
- package/lib/types/client/QuestionNavStrip.d.ts +48 -0
- package/lib/types/client/components/ConfigPanel.d.ts +14 -0
- package/lib/types/client/components/DotRail.d.ts +15 -0
- package/lib/types/client/components/NavOverlay.d.ts +30 -0
- package/lib/types/client/components/NavRail.d.ts +16 -0
- package/lib/types/client/components/RailDispatch.d.ts +8 -0
- package/lib/types/client/components/RailHost.d.ts +27 -0
- package/lib/types/client/components/SearchBox.d.ts +9 -0
- package/lib/types/client/components/SettingsPage.d.ts +9 -0
- package/lib/types/client/components/StandaloneSettings.d.ts +13 -0
- package/lib/types/client/components/codex/CodexRail.d.ts +3 -0
- package/lib/types/client/components/deepseek/DeepseekRail.d.ts +3 -0
- package/lib/types/client/components/minimal/MinimalRail.d.ts +8 -0
- package/lib/types/client/components/minimal/cards.d.ts +19 -0
- package/lib/types/client/components/minimal/dot.d.ts +14 -0
- package/lib/types/client/components/minimal/hint.d.ts +11 -0
- package/lib/types/client/components/minimal/paging.d.ts +10 -0
- package/lib/types/client/components/minimal/quick-settings.d.ts +8 -0
- package/lib/types/client/components/minimal/search.d.ts +27 -0
- package/lib/types/client/components/minimal/useRail.d.ts +90 -0
- package/lib/types/client/components/rail/cards.d.ts +19 -0
- package/lib/types/client/components/rail/hint.d.ts +11 -0
- package/lib/types/client/components/rail/paging.d.ts +10 -0
- package/lib/types/client/components/rail/quick-settings.d.ts +8 -0
- package/lib/types/client/components/rail/search.d.ts +34 -0
- package/lib/types/client/components/rail/useRail.d.ts +132 -0
- package/lib/types/client/components/settings/SettingsPage.d.ts +9 -0
- package/lib/types/client/components/settings/SettingsTab.d.ts +6 -0
- package/lib/types/client/components/settings/StandaloneSettings.d.ts +13 -0
- package/lib/types/client/components/shared/Cascade.d.ts +14 -0
- package/lib/types/client/components/shared/ConfigPanel.d.ts +8 -0
- package/lib/types/client/components/shared/Dot.d.ts +17 -0
- package/lib/types/client/components/shared/Hint.d.ts +6 -0
- package/lib/types/client/components/shared/PagingButton.d.ts +10 -0
- package/lib/types/client/components/shared/RailShell.d.ts +43 -0
- package/lib/types/client/components/shared/SearchBox.d.ts +18 -0
- package/lib/types/client/components/shared/hooks.d.ts +64 -0
- package/lib/types/client/components/shared/types.d.ts +101 -0
- package/lib/types/client/config.d.ts +9 -0
- package/lib/types/client/engines/jump.d.ts +17 -0
- package/lib/types/client/engines/useAtBottom.d.ts +1 -0
- package/lib/types/client/engines/useJump.d.ts +16 -0
- package/lib/types/client/engines/useScrollSpy.d.ts +12 -0
- package/lib/types/client/hooks.d.ts +7 -0
- package/lib/types/client/index.d.ts +31 -0
- package/lib/types/client/jump.d.ts +47 -0
- package/lib/types/client/locales.d.ts +142 -0
- package/lib/types/client/pure.d.ts +21 -0
- package/lib/types/client/settings.d.ts +173 -0
- package/lib/types/client/virtual/VirtualList.d.ts +15 -0
- package/lib/types/compat.d.ts +44 -0
- package/lib/types/config.d.ts +44 -0
- package/lib/types/core/active-dot.d.ts +15 -0
- package/lib/types/core/align.d.ts +10 -0
- package/lib/types/core/config.d.ts +44 -0
- package/lib/types/core/focus.d.ts +24 -0
- package/lib/types/core/jump.d.ts +39 -0
- package/lib/types/core/logic/active-dot.d.ts +15 -0
- package/lib/types/core/logic/focus.d.ts +22 -0
- package/lib/types/core/logic/jump.d.ts +39 -0
- package/lib/types/core/logic/nodes.d.ts +48 -0
- package/lib/types/core/model/nav-item.d.ts +12 -0
- package/lib/types/core/model/turns.d.ts +30 -0
- package/lib/types/core/nav-item.d.ts +12 -0
- package/lib/types/core/nodes.d.ts +43 -0
- package/lib/types/core/page-size.d.ts +8 -0
- package/lib/types/core/question-entry.d.ts +12 -0
- package/lib/types/core/time.d.ts +4 -0
- package/lib/types/core/turn-dots.d.ts +29 -0
- package/lib/types/core/utils/measure.d.ts +12 -0
- package/lib/types/core/utils/time.d.ts +4 -0
- package/lib/types/index.d.ts +4 -0
- package/lib/types/nav-item.d.ts +12 -0
- package/lib/types/nodes.d.ts +48 -0
- package/lib/types/projection.d.ts +71 -0
- package/lib/types/schema.d.ts +37 -0
- package/lib/types/search.d.ts +42 -0
- package/lib/types/settings.d.ts +99 -0
- package/package.json +71 -0
- package/src/client/components/RailDispatch.tsx +27 -0
- package/src/client/components/codex/CodexRail.tsx +348 -0
- package/src/client/components/deepseek/DeepseekRail.tsx +522 -0
- package/src/client/components/minimal/MinimalRail.tsx +324 -0
- package/src/client/components/minimal/dot.tsx +32 -0
- package/src/client/components/rail/cards.tsx +102 -0
- package/src/client/components/rail/hint.tsx +50 -0
- package/src/client/components/rail/paging.tsx +26 -0
- package/src/client/components/rail/quick-settings.tsx +130 -0
- package/src/client/components/rail/search.tsx +147 -0
- package/src/client/components/rail/useRail.ts +906 -0
- package/src/client/components/settings/SettingsPage.tsx +198 -0
- package/src/client/components/settings/StandaloneSettings.tsx +28 -0
- package/src/client/components/shared/types.ts +99 -0
- package/src/client/index.ts +388 -0
- package/src/client/jump.ts +293 -0
- package/src/client/locales.ts +143 -0
- package/src/client/settings.ts +482 -0
- package/src/client/styles.css +1580 -0
- package/src/compat.ts +161 -0
- package/src/index.ts +66 -0
- package/src/projection.ts +174 -0
- package/src/search.ts +291 -0
- package/src/settings.ts +169 -0
package/lib/compat.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
const VERSION_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
2
|
+
/** 解析 dsh 版本串;非法返回 null(不支持 x.y / 纯数字 etc.)。 */
|
|
3
|
+
export function parseDshVersion(v) {
|
|
4
|
+
const m = VERSION_RE.exec(v.trim());
|
|
5
|
+
if (!m)
|
|
6
|
+
return null;
|
|
7
|
+
const pre = m[4] !== undefined ? m[4].split('.') : null;
|
|
8
|
+
if (pre !== null && pre.some((s) => s === ''))
|
|
9
|
+
return null;
|
|
10
|
+
return { base: [Number(m[1]), Number(m[2]), Number(m[3])], pre };
|
|
11
|
+
}
|
|
12
|
+
function compareIdent(a, b) {
|
|
13
|
+
const na = /^\d+$/.test(a);
|
|
14
|
+
const nb = /^\d+$/.test(b);
|
|
15
|
+
if (na && nb) {
|
|
16
|
+
const x = Number(a);
|
|
17
|
+
const y = Number(b);
|
|
18
|
+
return x === y ? 0 : x < y ? -1 : 1;
|
|
19
|
+
}
|
|
20
|
+
if (na)
|
|
21
|
+
return -1; // 数字标识 < 字母标识
|
|
22
|
+
if (nb)
|
|
23
|
+
return 1;
|
|
24
|
+
return a === b ? 0 : a < b ? -1 : 1;
|
|
25
|
+
}
|
|
26
|
+
function comparePre(a, b) {
|
|
27
|
+
const n = Math.min(a.length, b.length);
|
|
28
|
+
for (let i = 0; i < n; i++) {
|
|
29
|
+
const c = compareIdent(a[i], b[i]);
|
|
30
|
+
if (c !== 0)
|
|
31
|
+
return c;
|
|
32
|
+
}
|
|
33
|
+
return a.length === b.length ? 0 : a.length < b.length ? -1 : 1;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 语义化比较:a < b → -1;相等 → 0;a > b → 1;任一无法解析 → NaN。
|
|
37
|
+
*/
|
|
38
|
+
export function compareDshVersions(a, b) {
|
|
39
|
+
const pa = parseDshVersion(a);
|
|
40
|
+
const pb = parseDshVersion(b);
|
|
41
|
+
if (pa === null || pb === null)
|
|
42
|
+
return NaN;
|
|
43
|
+
for (let i = 0; i < 3; i++) {
|
|
44
|
+
if (pa.base[i] !== pb.base[i])
|
|
45
|
+
return pa.base[i] < pb.base[i] ? -1 : 1;
|
|
46
|
+
}
|
|
47
|
+
if (pa.pre === null && pb.pre === null)
|
|
48
|
+
return 0;
|
|
49
|
+
if (pa.pre === null)
|
|
50
|
+
return 1; // 正式版高于同基线任意预发布
|
|
51
|
+
if (pb.pre === null)
|
|
52
|
+
return -1;
|
|
53
|
+
return comparePre(pa.pre, pb.pre);
|
|
54
|
+
}
|
|
55
|
+
/** 操作符比较(非法版本一律 false)。 */
|
|
56
|
+
export function checkDshVersion(current, op, target) {
|
|
57
|
+
const c = compareDshVersions(current, target);
|
|
58
|
+
if (Number.isNaN(c))
|
|
59
|
+
return false;
|
|
60
|
+
switch (op) {
|
|
61
|
+
case '>':
|
|
62
|
+
return c > 0;
|
|
63
|
+
case '>=':
|
|
64
|
+
return c >= 0;
|
|
65
|
+
case '<':
|
|
66
|
+
return c < 0;
|
|
67
|
+
case '<=':
|
|
68
|
+
return c <= 0;
|
|
69
|
+
default:
|
|
70
|
+
return c === 0;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** 标识段匹配:'*' 吞掉 ≥1 个剩余标识;静态段全等(全数字按数值比较)。 */
|
|
74
|
+
function matchIdents(cur, pat) {
|
|
75
|
+
const f = (i, j) => {
|
|
76
|
+
if (i === cur.length && j === pat.length)
|
|
77
|
+
return true;
|
|
78
|
+
if (j === pat.length)
|
|
79
|
+
return false;
|
|
80
|
+
if (pat[j] === '*') {
|
|
81
|
+
if (i >= cur.length)
|
|
82
|
+
return false; // * 至少匹配 1 个
|
|
83
|
+
for (let k = i + 1; k <= cur.length; k++) {
|
|
84
|
+
if (f(k, j + 1))
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (i >= cur.length)
|
|
90
|
+
return false;
|
|
91
|
+
if (compareIdent(pat[j], cur[i]) !== 0)
|
|
92
|
+
return false;
|
|
93
|
+
return f(i + 1, j + 1);
|
|
94
|
+
};
|
|
95
|
+
return f(0, 0);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 通配相等(仅配合 op='='):pattern 形如 0.1.1-*(任意预发布)、0.1.1-rc.*、
|
|
99
|
+
* 0.1.*(任意小版本)等;'*' 匹配 ≥1 个标识。例:0.1.1-* 命中 0.1.1-rc.1 / 0.1.1-rc.2。
|
|
100
|
+
*/
|
|
101
|
+
export function wildcardEqual(version, pattern) {
|
|
102
|
+
const pv = parseDshVersion(version);
|
|
103
|
+
if (pv === null)
|
|
104
|
+
return false;
|
|
105
|
+
const dash = pattern.indexOf('-');
|
|
106
|
+
const basePat = (dash >= 0 ? pattern.slice(0, dash) : pattern).split('.');
|
|
107
|
+
const prePat = dash >= 0 ? pattern.slice(dash + 1).split('.') : null;
|
|
108
|
+
if (basePat.length !== 3)
|
|
109
|
+
return false;
|
|
110
|
+
const baseHasStar = basePat.some((t) => t === '*');
|
|
111
|
+
for (let i = 0; i < 3; i++) {
|
|
112
|
+
const t = basePat[i];
|
|
113
|
+
if (t === '*')
|
|
114
|
+
continue;
|
|
115
|
+
if (!/^\d+$/.test(t))
|
|
116
|
+
return false;
|
|
117
|
+
if (Number(t) !== pv.base[i])
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
if (prePat === null) {
|
|
121
|
+
// 无预发布子句:基线精确时要求同为正式版;基线含通配(0.1.*)则任意预发布也算命中
|
|
122
|
+
return baseHasStar || pv.pre === null;
|
|
123
|
+
}
|
|
124
|
+
if (pv.pre === null)
|
|
125
|
+
return false; // 模式含预发布通配:当前必须有预发布
|
|
126
|
+
return matchIdents(pv.pre, prePat);
|
|
127
|
+
}
|
|
128
|
+
/** 单项命中:target 含 '*' 时仅接受 op='=' 的通配比较,否则按操作符数值比较。 */
|
|
129
|
+
function entryPasses(current, op, target) {
|
|
130
|
+
if (target.includes('*')) {
|
|
131
|
+
if (op !== '=')
|
|
132
|
+
return false;
|
|
133
|
+
return wildcardEqual(current, target);
|
|
134
|
+
}
|
|
135
|
+
return checkDshVersion(current, op, target);
|
|
136
|
+
}
|
|
137
|
+
/** 策略校验:target 为数组时命中任一即通过;通配(如 0.1.1-*)仅在 op='=' 时参与。 */
|
|
138
|
+
export function checkDshPolicy(current, policy) {
|
|
139
|
+
const targets = Array.isArray(policy.target) ? policy.target : [policy.target];
|
|
140
|
+
return targets.some((t) => entryPasses(current, policy.op, t));
|
|
141
|
+
}
|
|
142
|
+
/** 不兼容时的控制台提示文案(xxx = 检测到的当前 dsh 版本)。 */
|
|
143
|
+
export function dshCompatMessage(pluginName, currentDshVersion) {
|
|
144
|
+
return pluginName + ' 可能不适配 dsh ' + currentDshVersion + ' 版本,请慎重使用';
|
|
145
|
+
}
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// 导航条配置常量(纯数据):host settings schema 与 client 设置面共用,避免硬编码
|
|
2
|
+
// 每组:选项表 → 类型 → 默认值;键名常量集中在文件尾部
|
|
3
|
+
/** 风格选项(当前仅「简约」;后续风格在组件层扩展) */
|
|
4
|
+
export const StyleOptions = ['minimal'];
|
|
5
|
+
export const DEFAULT_STYLE = 'minimal';
|
|
6
|
+
/** 停靠侧选项与默认值 */
|
|
7
|
+
export const AlignOptions = ['left', 'right'];
|
|
8
|
+
export const DEFAULT_RAIL_ALIGN = 'left';
|
|
9
|
+
/** 节点栏条带高度档位(决定当前可见节点数:≈10/15/20 个) */
|
|
10
|
+
export const BandHeightOptions = ['compact', 'standard', 'tall'];
|
|
11
|
+
export const DEFAULT_BAND = 'standard';
|
|
12
|
+
/** 档位 → 条带最大高度(px):节点行高 14px × 10/15/20 */
|
|
13
|
+
export const BAND_HEIGHT_PX = { compact: 140, standard: 210, tall: 280 };
|
|
14
|
+
/** 跳转动画方式 */
|
|
15
|
+
export const ScrollModeOptions = ['smooth', 'instant'];
|
|
16
|
+
export const DEFAULT_SCROLL_MODE = 'smooth';
|
|
17
|
+
/** 显示策略:常显 / 悬停浮现 / 隐藏 */
|
|
18
|
+
export const ShowModeOptions = ['show', 'peek', 'hide'];
|
|
19
|
+
export const DEFAULT_SHOW = 'show';
|
|
20
|
+
/** 悬停级联卡数量(1/3/5 张) */
|
|
21
|
+
export const CardCountOptions = [1, 3, 5];
|
|
22
|
+
export const DEFAULT_CARD_COUNT = 5;
|
|
23
|
+
/** 风格 → 能力表 */
|
|
24
|
+
export const STYLE_CAPABILITIES = {
|
|
25
|
+
minimal: { hoverCards: true },
|
|
26
|
+
};
|
|
27
|
+
/** 设置键名(嵌套 schema 字段名;client 桥写回与 host schema 共用) */
|
|
28
|
+
export const STYLE_KEY = 'style';
|
|
29
|
+
export const ALIGN_KEY = 'align';
|
|
30
|
+
export const BAND_KEY = 'bandHeight';
|
|
31
|
+
export const SCROLL_KEY = 'scrollBehavior';
|
|
32
|
+
export const SHOW_COUNT_KEY = 'showCount';
|
|
33
|
+
export const SHOW_SEARCH_KEY = 'showSearch';
|
|
34
|
+
export const SHOW_QUICK_KEY = 'showQuickSettings';
|
|
35
|
+
export const CARD_COUNT_KEY = 'cardCount';
|
|
36
|
+
/** 本插件设置命名空间(拼写而非导入:client bundle 不得依赖宿主包) */
|
|
37
|
+
export const MGS_NS = 'mega-chat-nav';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 选取标记圆点:阅读线之上(含)的最后一行为当前回合;
|
|
3
|
+
* 阅读线在所有行之上(开场问答之前)时取第一行兜底;无渲染行返回 null。
|
|
4
|
+
* 行按圆点顺序输入;扫描保留最后一个满足项,单条乱序测量不回退标记。
|
|
5
|
+
*/
|
|
6
|
+
export function nearestReadDot(rows, readingLine) {
|
|
7
|
+
if (rows.length === 0)
|
|
8
|
+
return null;
|
|
9
|
+
let active = null;
|
|
10
|
+
for (const row of rows) {
|
|
11
|
+
if (row.top <= readingLine)
|
|
12
|
+
active = row.key;
|
|
13
|
+
}
|
|
14
|
+
return active ?? rows[0].key;
|
|
15
|
+
}
|
|
16
|
+
/** 阅读线固定为视口顶部(0) */
|
|
17
|
+
export const READING_LINE_TOP = 0;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// 导航条停靠侧常量:host 设置 schema 与浏览器设置面共用(纯数据,无宿主依赖)
|
|
2
|
+
/** 可选停靠侧 */
|
|
3
|
+
export const AlignOptions = ['left', 'right'];
|
|
4
|
+
/** 默认停靠侧(设置文档无覆盖时) */
|
|
5
|
+
export const DEFAULT_RAIL_ALIGN = 'left';
|
|
6
|
+
/** 本插件设置命名空间(此处拼写而非导入:client bundle 不得依赖宿主包) */
|
|
7
|
+
export const MGS_NS = 'mega-chat-nav';
|
|
8
|
+
/** 设置字段:停靠侧 */
|
|
9
|
+
export const ALIGN_KEY = 'align';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// 导航条配置常量(纯数据):host settings schema 与 client 设置面共用,避免硬编码
|
|
2
|
+
// 每组:选项表 → 类型 → 默认值;键名常量集中在文件尾部
|
|
3
|
+
/** 风格选项(当前仅「简约」;后续风格在组件层扩展) */
|
|
4
|
+
export const StyleOptions = ['minimal'];
|
|
5
|
+
export const DEFAULT_STYLE = 'minimal';
|
|
6
|
+
/** 停靠侧选项与默认值 */
|
|
7
|
+
export const AlignOptions = ['left', 'right'];
|
|
8
|
+
export const DEFAULT_RAIL_ALIGN = 'left';
|
|
9
|
+
/** 节点栏条带高度档位(决定当前可见节点数:≈10/15/20 个) */
|
|
10
|
+
export const BandHeightOptions = ['compact', 'standard', 'tall'];
|
|
11
|
+
export const DEFAULT_BAND = 'standard';
|
|
12
|
+
/** 档位 → 条带最大高度(px):节点行高 14px × 10/15/20 */
|
|
13
|
+
export const BAND_HEIGHT_PX = { compact: 140, standard: 210, tall: 280 };
|
|
14
|
+
/** 跳转动画方式 */
|
|
15
|
+
export const ScrollModeOptions = ['smooth', 'instant'];
|
|
16
|
+
export const DEFAULT_SCROLL_MODE = 'smooth';
|
|
17
|
+
/** 显示策略:常显 / 悬停浮现 / 隐藏 */
|
|
18
|
+
export const ShowModeOptions = ['show', 'peek', 'hide'];
|
|
19
|
+
export const DEFAULT_SHOW = 'show';
|
|
20
|
+
/** 悬停级联卡数量(1/3/5 张) */
|
|
21
|
+
export const CardCountOptions = [1, 3, 5];
|
|
22
|
+
export const DEFAULT_CARD_COUNT = 5;
|
|
23
|
+
/** 风格 → 能力表 */
|
|
24
|
+
export const STYLE_CAPABILITIES = {
|
|
25
|
+
minimal: { hoverCards: true },
|
|
26
|
+
};
|
|
27
|
+
/** 设置键名(嵌套 schema 字段名;client 桥写回与 host schema 共用) */
|
|
28
|
+
export const STYLE_KEY = 'style';
|
|
29
|
+
export const ALIGN_KEY = 'align';
|
|
30
|
+
export const BAND_KEY = 'bandHeight';
|
|
31
|
+
export const SCROLL_KEY = 'scrollBehavior';
|
|
32
|
+
export const SHOW_COUNT_KEY = 'showCount';
|
|
33
|
+
export const SHOW_SEARCH_KEY = 'showSearch';
|
|
34
|
+
export const SHOW_QUICK_KEY = 'showQuickSettings';
|
|
35
|
+
export const CARD_COUNT_KEY = 'cardCount';
|
|
36
|
+
/** 本插件设置命名空间(拼写而非导入:client bundle 不得依赖宿主包) */
|
|
37
|
+
export const MGS_NS = 'mega-chat-nav';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// 聚焦模型:悬停圆点的邻域放大、级联卡尺寸与翻页步进(纯函数)
|
|
2
|
+
/** 选中点两侧保持放大的圆点数 */
|
|
3
|
+
export const FOCUS_RADIUS = 2;
|
|
4
|
+
/** 悬停放大倍数:仅选中圆点放大(邻居不再有放大特效) */
|
|
5
|
+
export const FOCUS_SCALES = [1.5];
|
|
6
|
+
/** 距离 → 聚焦档(窗口内 0..RADIUS,窗口外 null;供级联卡窗口使用) */
|
|
7
|
+
export function focusTier(distance) {
|
|
8
|
+
const d = Math.abs(distance);
|
|
9
|
+
return d > FOCUS_RADIUS ? null : d;
|
|
10
|
+
}
|
|
11
|
+
/** 距离 → 放大倍数:仅选中点(distance 0)放大,邻居/窗口外均为 1 */
|
|
12
|
+
export function focusScale(distance) {
|
|
13
|
+
const d = Math.abs(distance);
|
|
14
|
+
return d === 0 ? FOCUS_SCALES[0] : 1;
|
|
15
|
+
}
|
|
16
|
+
export function focusCardMetrics(distance) {
|
|
17
|
+
const tier = focusTier(distance);
|
|
18
|
+
if (tier === null)
|
|
19
|
+
return null;
|
|
20
|
+
switch (tier) {
|
|
21
|
+
case 0: return { widthPx: 380, fontSize: 13, maxLines: 6 };
|
|
22
|
+
case 1: return { widthPx: 300, fontSize: 12.5, maxLines: 2 };
|
|
23
|
+
default: return { widthPx: 240, fontSize: 12, maxLines: 1 };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** 基础圆点直径与间距(与轨样式一致,翻页步进按整行计) */
|
|
27
|
+
export const DOT_SIZE = 8;
|
|
28
|
+
export const DOT_GAP = 6;
|
|
29
|
+
/** 一次翻页揭示的整行数 */
|
|
30
|
+
export const DOT_PAGE_ROWS = 5;
|
|
31
|
+
/** 一次翻页的滚动步长(px) */
|
|
32
|
+
export function pageStep(dotSize = DOT_SIZE, gap = DOT_GAP, rows = DOT_PAGE_ROWS) {
|
|
33
|
+
return rows * (dotSize + gap);
|
|
34
|
+
}
|
|
35
|
+
/** 将目标滚动位置钳制到有效区间 */
|
|
36
|
+
export function clampScrollTop(target, maxScroll) {
|
|
37
|
+
return Math.max(0, Math.min(target, Math.max(0, maxScroll)));
|
|
38
|
+
}
|
package/lib/core/jump.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// 跳转编排:定位目标回合行,必要时按需向后扩窗(端口注入,可脱离浏览器测试)
|
|
2
|
+
import { nearestRenderable } from "./nodes.js";
|
|
3
|
+
const DEFAULT_LIMITS = {
|
|
4
|
+
totalTimeoutMs: 15_000,
|
|
5
|
+
maxPages: 100,
|
|
6
|
+
rowWaitMs: 8_000,
|
|
7
|
+
pollMs: 60,
|
|
8
|
+
};
|
|
9
|
+
function minAnchorSeq(rows) {
|
|
10
|
+
let min = null;
|
|
11
|
+
for (const row of rows) {
|
|
12
|
+
if (min === null || row.anchorSeq < min)
|
|
13
|
+
min = row.anchorSeq;
|
|
14
|
+
}
|
|
15
|
+
return min;
|
|
16
|
+
}
|
|
17
|
+
function renderableRows(rows) {
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const row of rows) {
|
|
20
|
+
if (row.visibility === 'hidden')
|
|
21
|
+
continue;
|
|
22
|
+
out.push({ key: row.key, anchorSeq: row.anchorSeq });
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 跳转到 key 对应行:先扩窗(预算内)直至目标入窗,再等待行渲染并滚动;
|
|
28
|
+
* 目标隐藏/缺失时回退最近可渲染行。各失败点返回结构化结果码。
|
|
29
|
+
*/
|
|
30
|
+
export async function jumpToTarget(ports, key, options = {}, mode = 'smooth') {
|
|
31
|
+
const limits = { ...DEFAULT_LIMITS, ...options };
|
|
32
|
+
const fail = (code, fallback = false) => {
|
|
33
|
+
ports.report?.(code, fallback);
|
|
34
|
+
return fallback ? { ok: false, code, fallback: true } : { ok: false, code };
|
|
35
|
+
};
|
|
36
|
+
if (!ports.isViewActive())
|
|
37
|
+
return fail('VIEW_INACTIVE');
|
|
38
|
+
const deadline = ports.now() + limits.totalTimeoutMs;
|
|
39
|
+
let pages = 0;
|
|
40
|
+
// 阶段一:向后扩窗直到目标出现在已加载窗口
|
|
41
|
+
for (;;) {
|
|
42
|
+
const snap = ports.snapshot();
|
|
43
|
+
if (snap === undefined)
|
|
44
|
+
return fail('VIEW_INACTIVE');
|
|
45
|
+
const rows = renderableRows(snap.rows);
|
|
46
|
+
if (rows.some((r) => r.key === key))
|
|
47
|
+
break;
|
|
48
|
+
if (snap.openState !== 'open') {
|
|
49
|
+
if (snap.openState === 'error' || ports.now() > deadline) {
|
|
50
|
+
return fail(snap.openState === 'error' ? 'VIEW_INACTIVE' : 'TIMEOUT');
|
|
51
|
+
}
|
|
52
|
+
await ports.sleep(limits.pollMs);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (snap.hasMore !== true)
|
|
56
|
+
return fail('NOT_FOUND');
|
|
57
|
+
if (pages >= limits.maxPages || ports.now() > deadline)
|
|
58
|
+
return fail('TIMEOUT');
|
|
59
|
+
if (snap.loadingOlder) {
|
|
60
|
+
await ports.sleep(limits.pollMs);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const before = minAnchorSeq(rows);
|
|
64
|
+
await ports.loadOlder();
|
|
65
|
+
pages += 1;
|
|
66
|
+
const afterSnap = ports.snapshot();
|
|
67
|
+
const after = minAnchorSeq(afterSnap === undefined ? [] : afterSnap.rows);
|
|
68
|
+
if (after === null || (before !== null && after >= before))
|
|
69
|
+
return fail('NOT_FOUND');
|
|
70
|
+
}
|
|
71
|
+
// 阶段二:等待行渲染后滚动;隐藏则回退最近可渲染行
|
|
72
|
+
const waitRendered = async (rowKey) => {
|
|
73
|
+
for (let waited = 0; waited <= limits.rowWaitMs; waited += limits.pollMs) {
|
|
74
|
+
if (!ports.isViewActive())
|
|
75
|
+
return null;
|
|
76
|
+
const row = ports.findRow(rowKey);
|
|
77
|
+
if (row !== null)
|
|
78
|
+
return row;
|
|
79
|
+
await ports.sleep(limits.pollMs);
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
};
|
|
83
|
+
const row = await waitRendered(key);
|
|
84
|
+
if (row !== null) {
|
|
85
|
+
ports.scrollIntoView(row, mode);
|
|
86
|
+
return { ok: true };
|
|
87
|
+
}
|
|
88
|
+
const snap = ports.snapshot();
|
|
89
|
+
const fallback = nearestRenderable(snap === undefined ? [] : snap.rows, key);
|
|
90
|
+
if (fallback !== null) {
|
|
91
|
+
const fbRow = await waitRendered(fallback.key);
|
|
92
|
+
if (fbRow !== null) {
|
|
93
|
+
ports.scrollIntoView(fbRow, mode);
|
|
94
|
+
return fail('TARGET_HIDDEN', true);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return fail('TARGET_HIDDEN', false);
|
|
98
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** 阅读线固定为视口顶部(0) */
|
|
2
|
+
export const READING_LINE_TOP = 0;
|
|
3
|
+
/**
|
|
4
|
+
* 选取标记圆点:阅读线之上(含)的最后一行为当前回合;
|
|
5
|
+
* 阅读线在所有行之上(开场问答之前)时取第一行兜底;无渲染行返回 null。
|
|
6
|
+
* 实现:自尾向首扫描第一个越过阅读线的行(等价于「最后一个满足项」)。
|
|
7
|
+
*/
|
|
8
|
+
export function nearestReadDot(rows, readingLine) {
|
|
9
|
+
if (rows.length === 0)
|
|
10
|
+
return null;
|
|
11
|
+
for (let i = rows.length - 1; i >= 0; i--) {
|
|
12
|
+
if (rows[i].top <= readingLine)
|
|
13
|
+
return rows[i].key;
|
|
14
|
+
}
|
|
15
|
+
return rows[0].key;
|
|
16
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// 聚焦模型(纯函数):悬停圆点的邻域档位、放大倍数与级联卡尺寸
|
|
2
|
+
/** 选中点两侧计入聚焦窗口的圆点数 */
|
|
3
|
+
export const FOCUS_RADIUS = 2;
|
|
4
|
+
/** 悬停放大倍数:仅选中圆点放大(邻居不再有放大特效) */
|
|
5
|
+
export const FOCUS_SCALES = [1.5];
|
|
6
|
+
/** 档位 → 卡尺寸表(档 0 选中卡 / 档 1 / 档 2 最窄) */
|
|
7
|
+
const CARD_SIZE_TABLE = [
|
|
8
|
+
{ widthPx: 380, fontSize: 13, maxLines: 6 },
|
|
9
|
+
{ widthPx: 300, fontSize: 12.5, maxLines: 2 },
|
|
10
|
+
{ widthPx: 240, fontSize: 12, maxLines: 1 },
|
|
11
|
+
];
|
|
12
|
+
/** 距离 → 聚焦度量(档位 + 放大倍数 + 卡尺寸);聚焦窗口外返回 null */
|
|
13
|
+
export function focusMetrics(distance) {
|
|
14
|
+
const magnitude = distance < 0 ? -distance : distance;
|
|
15
|
+
if (magnitude > FOCUS_RADIUS)
|
|
16
|
+
return null;
|
|
17
|
+
return {
|
|
18
|
+
tier: magnitude,
|
|
19
|
+
scale: magnitude === 0 ? FOCUS_SCALES[0] : 1,
|
|
20
|
+
card: CARD_SIZE_TABLE[magnitude] ?? CARD_SIZE_TABLE[CARD_SIZE_TABLE.length - 1],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** 距离 → 聚焦档(窗口内 0..RADIUS,窗口外 null;供级联卡窗口使用) */
|
|
24
|
+
export function focusTier(distance) {
|
|
25
|
+
return focusMetrics(distance)?.tier ?? null;
|
|
26
|
+
}
|
|
27
|
+
/** 距离 → 放大倍数:仅选中点(distance 0)放大,邻居/窗口外均为 1 */
|
|
28
|
+
export function focusScale(distance) {
|
|
29
|
+
return focusMetrics(distance)?.scale ?? 1;
|
|
30
|
+
}
|
|
31
|
+
/** 距离 → 卡呈现参数;聚焦窗口外返回 null */
|
|
32
|
+
export function focusCardMetrics(distance) {
|
|
33
|
+
return focusMetrics(distance)?.card ?? null;
|
|
34
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// 跳转编排:显式状态机驱动(阶段枚举 + 单步推进;端口注入,可脱离浏览器测试)
|
|
2
|
+
import { NavNodes } from "./nodes.js";
|
|
3
|
+
const DEFAULT_LIMITS = {
|
|
4
|
+
totalTimeoutMs: 15_000,
|
|
5
|
+
maxPages: 100,
|
|
6
|
+
rowWaitMs: 8_000,
|
|
7
|
+
pollMs: 60,
|
|
8
|
+
};
|
|
9
|
+
function createMachine(ports, key, mode, options) {
|
|
10
|
+
return {
|
|
11
|
+
stage: 'locate',
|
|
12
|
+
key,
|
|
13
|
+
mode,
|
|
14
|
+
deadline: ports.now() + (options.totalTimeoutMs ?? DEFAULT_LIMITS.totalTimeoutMs),
|
|
15
|
+
limits: {
|
|
16
|
+
totalTimeoutMs: options.totalTimeoutMs ?? DEFAULT_LIMITS.totalTimeoutMs,
|
|
17
|
+
maxPages: options.maxPages ?? DEFAULT_LIMITS.maxPages,
|
|
18
|
+
rowWaitMs: options.rowWaitMs ?? DEFAULT_LIMITS.rowWaitMs,
|
|
19
|
+
pollMs: options.pollMs ?? DEFAULT_LIMITS.pollMs,
|
|
20
|
+
},
|
|
21
|
+
pages: 0,
|
|
22
|
+
waited: 0,
|
|
23
|
+
rowKey: null,
|
|
24
|
+
onFallback: false,
|
|
25
|
+
result: null,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function settle(machine, ports, code, fallback = false) {
|
|
29
|
+
machine.stage = 'finished';
|
|
30
|
+
if (code === undefined) {
|
|
31
|
+
machine.result = { ok: true };
|
|
32
|
+
}
|
|
33
|
+
else if (fallback) {
|
|
34
|
+
machine.result = { ok: false, code, fallback: true };
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
machine.result = { ok: false, code };
|
|
38
|
+
}
|
|
39
|
+
if (code !== undefined)
|
|
40
|
+
ports.report?.(code, fallback);
|
|
41
|
+
}
|
|
42
|
+
/** 快照的可渲染行中是否已含目标 */
|
|
43
|
+
function containsTarget(rows, key) {
|
|
44
|
+
for (const row of rows) {
|
|
45
|
+
if (row.visibility !== 'hidden' && row.key === key)
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
/** 当前窗口最小锚点序号;空窗口返回 null */
|
|
51
|
+
function smallestAnchor(rows) {
|
|
52
|
+
let smallest = null;
|
|
53
|
+
for (const row of rows) {
|
|
54
|
+
if (smallest === null || row.anchorSeq < smallest)
|
|
55
|
+
smallest = row.anchorSeq;
|
|
56
|
+
}
|
|
57
|
+
return smallest;
|
|
58
|
+
}
|
|
59
|
+
/** 推进一个状态步;返回驱动层下一步动作(wait=轮询、load=执行扩窗、next=立即再推) */
|
|
60
|
+
function advance(machine, ports) {
|
|
61
|
+
switch (machine.stage) {
|
|
62
|
+
case 'locate': {
|
|
63
|
+
const snap = ports.snapshot();
|
|
64
|
+
if (snap === undefined) {
|
|
65
|
+
settle(machine, ports, 'VIEW_INACTIVE');
|
|
66
|
+
return { kind: 'next' };
|
|
67
|
+
}
|
|
68
|
+
if (containsTarget(snap.rows, machine.key)) {
|
|
69
|
+
machine.stage = 'wait-row';
|
|
70
|
+
machine.rowKey = machine.key;
|
|
71
|
+
machine.waited = 0;
|
|
72
|
+
return { kind: 'next' };
|
|
73
|
+
}
|
|
74
|
+
if (snap.openState !== 'open') {
|
|
75
|
+
if (snap.openState === 'error') {
|
|
76
|
+
settle(machine, ports, 'VIEW_INACTIVE');
|
|
77
|
+
return { kind: 'next' };
|
|
78
|
+
}
|
|
79
|
+
machine.stage = 'wait-open';
|
|
80
|
+
return { kind: 'wait' };
|
|
81
|
+
}
|
|
82
|
+
if (!snap.hasMore) {
|
|
83
|
+
settle(machine, ports, 'NOT_FOUND');
|
|
84
|
+
return { kind: 'next' };
|
|
85
|
+
}
|
|
86
|
+
machine.stage = 'page';
|
|
87
|
+
return { kind: 'next' };
|
|
88
|
+
}
|
|
89
|
+
case 'wait-open': {
|
|
90
|
+
if (ports.now() > machine.deadline) {
|
|
91
|
+
settle(machine, ports, 'TIMEOUT');
|
|
92
|
+
return { kind: 'next' };
|
|
93
|
+
}
|
|
94
|
+
const snap = ports.snapshot();
|
|
95
|
+
if (snap === undefined) {
|
|
96
|
+
settle(machine, ports, 'VIEW_INACTIVE');
|
|
97
|
+
return { kind: 'next' };
|
|
98
|
+
}
|
|
99
|
+
if (snap.openState === 'open') {
|
|
100
|
+
machine.stage = 'locate';
|
|
101
|
+
return { kind: 'next' };
|
|
102
|
+
}
|
|
103
|
+
if (snap.openState === 'error') {
|
|
104
|
+
settle(machine, ports, 'VIEW_INACTIVE');
|
|
105
|
+
return { kind: 'next' };
|
|
106
|
+
}
|
|
107
|
+
return { kind: 'wait' };
|
|
108
|
+
}
|
|
109
|
+
case 'page': {
|
|
110
|
+
if (machine.pages >= machine.limits.maxPages || ports.now() > machine.deadline) {
|
|
111
|
+
settle(machine, ports, 'TIMEOUT');
|
|
112
|
+
return { kind: 'next' };
|
|
113
|
+
}
|
|
114
|
+
const snap = ports.snapshot();
|
|
115
|
+
if (snap === undefined) {
|
|
116
|
+
settle(machine, ports, 'VIEW_INACTIVE');
|
|
117
|
+
return { kind: 'next' };
|
|
118
|
+
}
|
|
119
|
+
if (snap.loadingOlder)
|
|
120
|
+
return { kind: 'wait' };
|
|
121
|
+
return { kind: 'load', before: smallestAnchor(snap.rows) };
|
|
122
|
+
}
|
|
123
|
+
case 'wait-row': {
|
|
124
|
+
if (!ports.isViewActive()) {
|
|
125
|
+
settle(machine, ports, 'TARGET_HIDDEN');
|
|
126
|
+
return { kind: 'next' };
|
|
127
|
+
}
|
|
128
|
+
const row = machine.rowKey !== null ? ports.findRow(machine.rowKey) : null;
|
|
129
|
+
if (row !== null) {
|
|
130
|
+
ports.scrollIntoView(row, machine.mode);
|
|
131
|
+
settle(machine, ports, machine.onFallback ? 'TARGET_HIDDEN' : undefined, machine.onFallback);
|
|
132
|
+
return { kind: 'next' };
|
|
133
|
+
}
|
|
134
|
+
machine.waited += machine.limits.pollMs;
|
|
135
|
+
if (machine.waited > machine.limits.rowWaitMs) {
|
|
136
|
+
if (machine.onFallback) {
|
|
137
|
+
settle(machine, ports, 'TARGET_HIDDEN');
|
|
138
|
+
return { kind: 'next' };
|
|
139
|
+
}
|
|
140
|
+
const snap = ports.snapshot();
|
|
141
|
+
const nearest = NavNodes.nearest(snap === undefined ? [] : snap.rows, machine.key);
|
|
142
|
+
if (nearest === null) {
|
|
143
|
+
settle(machine, ports, 'TARGET_HIDDEN');
|
|
144
|
+
return { kind: 'next' };
|
|
145
|
+
}
|
|
146
|
+
machine.rowKey = nearest.key;
|
|
147
|
+
machine.onFallback = true;
|
|
148
|
+
machine.waited = 0;
|
|
149
|
+
return { kind: 'next' };
|
|
150
|
+
}
|
|
151
|
+
return { kind: 'wait' };
|
|
152
|
+
}
|
|
153
|
+
default:
|
|
154
|
+
return { kind: 'next' };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 跳转到 key 对应行:状态机驱动(定位 → 等就绪 → 扩窗 → 等渲染 → 回退);
|
|
159
|
+
* 各失败点返回结构化结果码。
|
|
160
|
+
*/
|
|
161
|
+
export async function jumpToTarget(ports, key, options = {}, mode = 'smooth') {
|
|
162
|
+
const machine = createMachine(ports, key, mode, options);
|
|
163
|
+
if (!ports.isViewActive()) {
|
|
164
|
+
settle(machine, ports, 'VIEW_INACTIVE');
|
|
165
|
+
return machine.result;
|
|
166
|
+
}
|
|
167
|
+
for (;;) {
|
|
168
|
+
if (machine.result !== null)
|
|
169
|
+
return machine.result;
|
|
170
|
+
const step = advance(machine, ports);
|
|
171
|
+
if (machine.result !== null)
|
|
172
|
+
return machine.result;
|
|
173
|
+
if (step.kind === 'wait') {
|
|
174
|
+
await ports.sleep(machine.limits.pollMs);
|
|
175
|
+
}
|
|
176
|
+
else if (step.kind === 'load') {
|
|
177
|
+
await ports.loadOlder();
|
|
178
|
+
machine.pages += 1;
|
|
179
|
+
const afterSnap = ports.snapshot();
|
|
180
|
+
const after = smallestAnchor(afterSnap === undefined ? [] : afterSnap.rows);
|
|
181
|
+
if (after === null || (step.before !== null && after >= step.before)) {
|
|
182
|
+
settle(machine, ports, 'NOT_FOUND');
|
|
183
|
+
return machine.result;
|
|
184
|
+
}
|
|
185
|
+
machine.stage = 'locate';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|