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
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import z from '@deepseek-ai/schemastery';
|
|
2
|
+
export declare const ConfigSchema: z<Schemastery.ObjectS<{
|
|
3
|
+
enabled: z<boolean, boolean>;
|
|
4
|
+
align: z<"left" | "right", "left" | "right">;
|
|
5
|
+
panelWidth: z<number, number>;
|
|
6
|
+
collapsed: z<boolean, boolean>;
|
|
7
|
+
anchorRule: z<"line40" | "top" | "center" | "lastInteraction", "line40" | "top" | "center" | "lastInteraction">;
|
|
8
|
+
followNewMessage: z<boolean, boolean>;
|
|
9
|
+
scrollBehavior: z<"smooth" | "instant", "smooth" | "instant">;
|
|
10
|
+
pulseMs: z<number, number>;
|
|
11
|
+
summaryLength: z<number, number>;
|
|
12
|
+
a11yShortcuts: z<boolean, boolean>;
|
|
13
|
+
}>, Schemastery.ObjectT<{
|
|
14
|
+
enabled: z<boolean, boolean>;
|
|
15
|
+
align: z<"left" | "right", "left" | "right">;
|
|
16
|
+
panelWidth: z<number, number>;
|
|
17
|
+
collapsed: z<boolean, boolean>;
|
|
18
|
+
anchorRule: z<"line40" | "top" | "center" | "lastInteraction", "line40" | "top" | "center" | "lastInteraction">;
|
|
19
|
+
followNewMessage: z<boolean, boolean>;
|
|
20
|
+
scrollBehavior: z<"smooth" | "instant", "smooth" | "instant">;
|
|
21
|
+
pulseMs: z<number, number>;
|
|
22
|
+
summaryLength: z<number, number>;
|
|
23
|
+
a11yShortcuts: z<boolean, boolean>;
|
|
24
|
+
}>>;
|
|
25
|
+
export interface NavConfig {
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
align: 'left' | 'right';
|
|
28
|
+
panelWidth: number;
|
|
29
|
+
collapsed: boolean;
|
|
30
|
+
anchorRule: 'line40' | 'top' | 'center' | 'lastInteraction';
|
|
31
|
+
followNewMessage: boolean;
|
|
32
|
+
scrollBehavior: 'smooth' | 'instant';
|
|
33
|
+
pulseMs: number;
|
|
34
|
+
summaryLength: number;
|
|
35
|
+
a11yShortcuts: boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare const defaultConfig: NavConfig;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
export interface SearchHit {
|
|
3
|
+
key: string;
|
|
4
|
+
seq: number;
|
|
5
|
+
time: number;
|
|
6
|
+
role: string;
|
|
7
|
+
/** 命中消息所在轮次(turn/start 计数;未定位为 0) */
|
|
8
|
+
turn: number;
|
|
9
|
+
/** 命中是否来自更早历史(非当前已加载窗口) */
|
|
10
|
+
historic: boolean;
|
|
11
|
+
snippet: string;
|
|
12
|
+
hitStart: number;
|
|
13
|
+
hitLen: number;
|
|
14
|
+
}
|
|
15
|
+
interface EventLike {
|
|
16
|
+
type?: string;
|
|
17
|
+
seq?: number;
|
|
18
|
+
time?: number;
|
|
19
|
+
data?: {
|
|
20
|
+
id?: string;
|
|
21
|
+
content?: unknown;
|
|
22
|
+
message?: {
|
|
23
|
+
id?: string;
|
|
24
|
+
content?: unknown;
|
|
25
|
+
};
|
|
26
|
+
source?: {
|
|
27
|
+
kind?: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** 事件 + 来源标注 */
|
|
32
|
+
interface SourcedEvent {
|
|
33
|
+
ev: EventLike;
|
|
34
|
+
/** 来自更早历史(持久化日志 / sessionQuery),而非当前已加载窗口 */
|
|
35
|
+
historic: boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare function searchEvents(events: readonly SourcedEvent[], q: string, limit: number, scopes?: readonly string[]): {
|
|
38
|
+
hits: SearchHit[];
|
|
39
|
+
truncated: boolean;
|
|
40
|
+
};
|
|
41
|
+
export declare function registerSearchRoute(ctx: Context): void;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-side durable settings for the mega-chat-nav plugin.
|
|
3
|
+
* 配置选项为本插件自有定义:host 半持有此处;client 半在
|
|
4
|
+
* src/client/settings.ts 持有同值副本(client bundle 不得依赖宿主包)。
|
|
5
|
+
*
|
|
6
|
+
* @module dsh-mega-chat-nav/settings
|
|
7
|
+
*/
|
|
8
|
+
import z from '@deepseek-ai/schemastery';
|
|
9
|
+
import type { SettingsNamespace } from '@deepseek-ai/dsh-settings';
|
|
10
|
+
/** 风格选项:简约 / codex(时间线刻度)/ deepseek(用户消息面板,常态短横折叠) */
|
|
11
|
+
export declare const STYLE_OPTIONS: readonly ["minimal", "codex", "deepseek"];
|
|
12
|
+
export type NavStyle = typeof STYLE_OPTIONS[number];
|
|
13
|
+
/** 停靠侧选项 */
|
|
14
|
+
export declare const ALIGN_OPTIONS: readonly ["left", "right"];
|
|
15
|
+
export type RailAlign = typeof ALIGN_OPTIONS[number];
|
|
16
|
+
/** 节点栏条带高度档位 */
|
|
17
|
+
export declare const BAND_OPTIONS: readonly ["compact", "standard", "tall"];
|
|
18
|
+
export type BandHeight = typeof BAND_OPTIONS[number];
|
|
19
|
+
/** 跳转动画方式 */
|
|
20
|
+
export declare const SCROLL_OPTIONS: readonly ["smooth", "instant"];
|
|
21
|
+
export type ScrollMode = typeof SCROLL_OPTIONS[number];
|
|
22
|
+
/** 显示策略:常显 / 悬停浮现 / 隐藏 */
|
|
23
|
+
export declare const SHOW_OPTIONS: readonly ["show", "peek", "hide"];
|
|
24
|
+
export type ShowMode = typeof SHOW_OPTIONS[number];
|
|
25
|
+
/** 翻页标记默认显示模式(按风格独立):minimal 常显(历史行为)/ codex 隐藏 */
|
|
26
|
+
export declare const DEFAULT_PAGING: Record<NavStyle, ShowMode>;
|
|
27
|
+
/** 搜索内容范围(多选;用户必选) */
|
|
28
|
+
export declare const SEARCH_SCOPE_OPTIONS: readonly ["user", "assistant", "tool"];
|
|
29
|
+
export type SearchScope = typeof SEARCH_SCOPE_OPTIONS[number];
|
|
30
|
+
export declare const DEFAULT_SEARCH_SCOPES: SearchScope[];
|
|
31
|
+
/** 悬停卡片数量 */
|
|
32
|
+
export declare const CARD_OPTIONS: readonly [1, 3, 5];
|
|
33
|
+
export type CardCount = typeof CARD_OPTIONS[number];
|
|
34
|
+
/** 悬浮卡默认数量(按风格独立):minimal 3 张 / codex 1 张 / deepseek 1 张(无悬浮卡能力,占位) */
|
|
35
|
+
export declare const DEFAULT_CARD_COUNT: Record<NavStyle, CardCount>;
|
|
36
|
+
/** 悬浮卡内容块(多选) */
|
|
37
|
+
export declare const CARD_ITEM_OPTIONS: readonly ["turn", "duration", "favorite"];
|
|
38
|
+
export type CardItem = typeof CARD_ITEM_OPTIONS[number];
|
|
39
|
+
export declare const DEFAULT_CARD_ITEMS: CardItem[];
|
|
40
|
+
/** 刻度色调:柔和(低对比)/ 深邃(高对比、清晰) */
|
|
41
|
+
export declare const MARK_TONE_OPTIONS: readonly ["soft", "deep"];
|
|
42
|
+
export type MarkTone = typeof MARK_TONE_OPTIONS[number];
|
|
43
|
+
export declare const DEFAULT_MARK_TONE: MarkTone;
|
|
44
|
+
export declare const DEFAULT_STYLE: NavStyle;
|
|
45
|
+
export declare const DEFAULT_ALIGN: RailAlign;
|
|
46
|
+
export declare const DEFAULT_BAND: BandHeight;
|
|
47
|
+
export declare const DEFAULT_SCROLL: ScrollMode;
|
|
48
|
+
export declare const DEFAULT_SHOW: ShowMode;
|
|
49
|
+
/** 通用配置(全局,跨风格生效) */
|
|
50
|
+
export interface NavGeneralSettings {
|
|
51
|
+
/** 当前风格 */
|
|
52
|
+
style: NavStyle;
|
|
53
|
+
/** 导航条停靠侧 */
|
|
54
|
+
align: RailAlign;
|
|
55
|
+
/** 跳转动画方式 */
|
|
56
|
+
scrollBehavior: ScrollMode;
|
|
57
|
+
/** 轮次总数徽标显示模式(兼容旧布尔:true→show / false→hide) */
|
|
58
|
+
showCount: ShowMode | boolean;
|
|
59
|
+
/** 搜索按钮显示模式(兼容旧布尔) */
|
|
60
|
+
showSearch: ShowMode | boolean;
|
|
61
|
+
/** 设置按钮显示模式(兼容旧布尔) */
|
|
62
|
+
showQuickSettings: ShowMode | boolean;
|
|
63
|
+
/** 收藏按钮显示模式(兼容旧布尔) */
|
|
64
|
+
showFavorites: ShowMode | boolean;
|
|
65
|
+
/** 搜索内容范围(多选;用户必选,其余可关) */
|
|
66
|
+
searchScopes: SearchScope[];
|
|
67
|
+
/** 导航栏与对话列边缘的偏移距离(px,0-32,随左右对齐自适应) */
|
|
68
|
+
railOffset: number;
|
|
69
|
+
/** @deprecated 旧版字段(翻页标记曾置于此处)——仅兼容存量配置,新主源在 styles.<style>.showPaging */
|
|
70
|
+
showPaging?: ShowMode | boolean;
|
|
71
|
+
}
|
|
72
|
+
/** 单风格配置(每风格独立一套) */
|
|
73
|
+
export interface StyleSettings {
|
|
74
|
+
/** 节点栏条带高度档位 */
|
|
75
|
+
bandHeight: BandHeight;
|
|
76
|
+
/** 悬停卡片数量(1/3/5) */
|
|
77
|
+
cardCount: CardCount;
|
|
78
|
+
/** 悬浮卡内容块(多选:轮次 / 用时 / 收藏) */
|
|
79
|
+
cardItems: CardItem[];
|
|
80
|
+
/** 刻度色调(codex 刻度轨使用:柔和 / 深邃) */
|
|
81
|
+
markTone: MarkTone;
|
|
82
|
+
/** 翻页标记显示模式:show=常显 / peek=浮现 / hide=隐藏 */
|
|
83
|
+
showPaging: ShowMode;
|
|
84
|
+
}
|
|
85
|
+
/** 风格专属配置(styles.<style> 每风格独立) */
|
|
86
|
+
export interface NavStyleSettings {
|
|
87
|
+
minimal: StyleSettings;
|
|
88
|
+
codex: StyleSettings;
|
|
89
|
+
deepseek: StyleSettings;
|
|
90
|
+
}
|
|
91
|
+
/** 持久化设置文档结构 */
|
|
92
|
+
export interface NavSettings {
|
|
93
|
+
general: NavGeneralSettings;
|
|
94
|
+
styles: NavStyleSettings;
|
|
95
|
+
}
|
|
96
|
+
/** 持久化设置 schema;也是浏览器 scope 校验的 wire 信封 */
|
|
97
|
+
export declare const NavSettingsSchema: z<NavSettings>;
|
|
98
|
+
/** 本插件设置命名空间 */
|
|
99
|
+
export declare const megaChatNavSettingsNamespace: SettingsNamespace;
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-mega-chat-nav",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "DSH mega 系列:会话提问圆点导航条(question-nav 移植 · mgcn 族)",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/types/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": { "types": "./lib/types/index.d.ts", "default": "./lib/index.js" },
|
|
9
|
+
"./client": { "types": "./lib/types/client/index.d.ts", "default": "./lib/client.js" },
|
|
10
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"files": ["lib", "src", "cordis.patch.yml", "README.md", "README.en.md"],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc -p tsconfig.json && node scripts/build-client.mjs",
|
|
16
|
+
"prepack": "npm run build",
|
|
17
|
+
"test": "vitest run tests"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
21
|
+
"zod": "^4.5.4"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
25
|
+
"@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
|
|
26
|
+
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
27
|
+
"@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
|
|
28
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.1-rc.2",
|
|
29
|
+
"@deepseek-ai/dsh-session": "^0.1.1-rc.2",
|
|
30
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
|
|
31
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.1-rc.2",
|
|
32
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2",
|
|
33
|
+
"react": "^18.2.0",
|
|
34
|
+
"react-dom": "^18.2.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependenciesMeta": {
|
|
37
|
+
"react": { "optional": true },
|
|
38
|
+
"react-dom": { "optional": true }
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"typescript": "^5.6.0",
|
|
42
|
+
"@types/node": "^22.0.0",
|
|
43
|
+
"vitest": "^2.1.0",
|
|
44
|
+
"esbuild": "^0.24.0",
|
|
45
|
+
"react": "^18.2.0",
|
|
46
|
+
"react-dom": "^18.2.0",
|
|
47
|
+
"@types/react": "^18.3.0",
|
|
48
|
+
"@types/react-dom": "^18.3.0",
|
|
49
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
50
|
+
"@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
|
|
51
|
+
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
52
|
+
"@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
|
|
53
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.1-rc.2",
|
|
54
|
+
"@deepseek-ai/dsh-session": "^0.1.1-rc.2",
|
|
55
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.1-rc.2",
|
|
57
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2"
|
|
58
|
+
},
|
|
59
|
+
"dsh": {
|
|
60
|
+
"bundle": { "patch": "./cordis.patch.yml" },
|
|
61
|
+
"client": {
|
|
62
|
+
"inject": [
|
|
63
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
65
|
+
"@deepseek-ai/dsh-client-locale",
|
|
66
|
+
"@deepseek-ai/dsh-client-modules"
|
|
67
|
+
],
|
|
68
|
+
"platform": "web"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// 风格分发:按当前配置的 style 挂载对应风格轨组件(minimal 圆点轨 / codex 刻度轨)。
|
|
2
|
+
// 风格配置各自独立(styles.<style> 段),此处仅做选择——切换风格即卸载/重挂风格轨。
|
|
3
|
+
import { useEffect, useState, type ReactNode } from 'react'
|
|
4
|
+
import type { NavStyle } from '../settings.ts'
|
|
5
|
+
import { DEFAULT_STYLE } from '../settings.ts'
|
|
6
|
+
import type { NavInjected, SessionSelector, Translate } from './shared/types.ts'
|
|
7
|
+
import { MinimalRail } from './minimal/MinimalRail.tsx'
|
|
8
|
+
import { CodexRail } from './codex/CodexRail.tsx'
|
|
9
|
+
import { DeepseekRail } from './deepseek/DeepseekRail.tsx'
|
|
10
|
+
|
|
11
|
+
export interface RailDispatchProps {
|
|
12
|
+
injected: NavInjected | undefined
|
|
13
|
+
useSessions?: SessionSelector
|
|
14
|
+
t: Translate
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function RailDispatch(props: RailDispatchProps): ReactNode {
|
|
18
|
+
const [style, setStyle] = useState<NavStyle>(() => props.injected?.style() ?? DEFAULT_STYLE)
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (props.injected === undefined) return
|
|
21
|
+
return props.injected.subscribeSettings(() => setStyle(props.injected?.style() ?? DEFAULT_STYLE))
|
|
22
|
+
}, [props.injected])
|
|
23
|
+
const railProps = { injected: props.injected, useSessions: props.useSessions, t: props.t }
|
|
24
|
+
if (style === 'codex') return <CodexRail {...railProps} />
|
|
25
|
+
if (style === 'deepseek') return <DeepseekRail {...railProps} />
|
|
26
|
+
return <MinimalRail {...railProps} />
|
|
27
|
+
}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
// codex 风格:刻度轨形态(时间线式)——风格自治实现(数据/布局/间谍/翻页经 ../rail 共享,
|
|
2
|
+
// 本目录自持轨道视觉与 hover 波纹动效;悬浮卡与操作区为跨风格能力组件)
|
|
3
|
+
//
|
|
4
|
+
// 视觉语言(吸收内化,不复刻类名):竖向轨道一条淡引导线贯穿条带;每轮一个
|
|
5
|
+
// 水平刻度槽(slot),内含一条短横刻度(mark)。刻度静止为短灰条;hover 时
|
|
6
|
+
// 本尊刻度横向展开为长亮条,向两侧逐级衰减(±1 中长 / ±2 中短 / ±3 短),
|
|
7
|
+
// 形成波纹式展开;阅读位置(current)刻度常亮;跳转目标刻度品牌色闪光。
|
|
8
|
+
// 行距 14px 与圆点轨一致(翻页步距 / 跟读滚动对两风格通用)。
|
|
9
|
+
import type { ReactNode } from 'react'
|
|
10
|
+
import { memo, useEffect, useRef, useState } from 'react'
|
|
11
|
+
import { useMarkerData, usePinning, useReadingSpy, useBandPaging, useCardPreview, useFailureNotice, useLoadingNotice, useFavorites, useMobileMode, useMobileSearchButton, useNavSettings, clockText, ROW_HEIGHT, usePeekState, useClosePopoversOnOutside } from '../rail/useRail.ts'
|
|
12
|
+
import { Cards } from '../rail/cards.tsx'
|
|
13
|
+
import { PagingButton } from '../rail/paging.tsx'
|
|
14
|
+
import { Hint } from '../rail/hint.tsx'
|
|
15
|
+
import { SearchBox } from '../rail/search.tsx'
|
|
16
|
+
import { QuickSettings } from '../rail/quick-settings.tsx'
|
|
17
|
+
import { STYLE_CAPABILITIES, BAND_HEIGHT_PX } from '../../settings.ts'
|
|
18
|
+
import type { RailMarker, RailProps } from '../shared/types.ts'
|
|
19
|
+
|
|
20
|
+
/** 刻度槽组件:纯展示(hover 波纹由 CSS 级联完成,React 不参与逐槽状态)——
|
|
21
|
+
* memo 化后只有 current/jumping/focused/reveal 变化才重渲,长会话 hover 不再整列刷新 */
|
|
22
|
+
const CodexSlot = memo(function CodexSlot(props: {
|
|
23
|
+
marker: RailMarker
|
|
24
|
+
current: boolean
|
|
25
|
+
focused: boolean
|
|
26
|
+
jumping: boolean
|
|
27
|
+
label: string
|
|
28
|
+
reveal?: { dir: 1 | -1; order: number }
|
|
29
|
+
}): ReactNode {
|
|
30
|
+
const { marker, current, focused, jumping, label, reveal } = props
|
|
31
|
+
const cls = ['mgcn-cxSlot']
|
|
32
|
+
if (focused) cls.push('mgcn-cxFocused')
|
|
33
|
+
if (current) cls.push('mgcn-cxCurrent')
|
|
34
|
+
if (jumping) cls.push('mgcn-cxJump')
|
|
35
|
+
if (reveal !== undefined) cls.push(reveal.dir === 1 ? 'mgcn-cxEnterFromBottom' : 'mgcn-cxEnterFromTop')
|
|
36
|
+
return (
|
|
37
|
+
<button
|
|
38
|
+
type="button"
|
|
39
|
+
className={cls.join(' ')}
|
|
40
|
+
style={reveal !== undefined ? ({ animationDelay: reveal.order * 35 + 'ms' } as React.CSSProperties) : undefined}
|
|
41
|
+
data-mega-chat-nav-key={marker.key}
|
|
42
|
+
aria-label={label}
|
|
43
|
+
>
|
|
44
|
+
<i className="mgcn-cxMark" aria-hidden="true" />
|
|
45
|
+
</button>
|
|
46
|
+
)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
/** 阅读位置跟随:正文滚动时带内刻度平滑居中(点击跳转后不冻结) */
|
|
50
|
+
function useFollowCurrent(
|
|
51
|
+
listRef: { current: HTMLDivElement | null },
|
|
52
|
+
currentKey: string | null,
|
|
53
|
+
markers: readonly RailMarker[],
|
|
54
|
+
resetKey?: boolean | number | string,
|
|
55
|
+
): void {
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const list = listRef.current
|
|
58
|
+
if (list === null || currentKey === null) return
|
|
59
|
+
const index = markers.findIndex((d) => d.key === currentKey)
|
|
60
|
+
if (index < 0) return
|
|
61
|
+
const slotTop = 8 + index * ROW_HEIGHT
|
|
62
|
+
const contentHeight = 16 + markers.length * ROW_HEIGHT
|
|
63
|
+
const maxScroll = Math.max(0, contentHeight - list.clientHeight)
|
|
64
|
+
const target = Math.max(0, Math.min(slotTop - list.clientHeight / 2 + ROW_HEIGHT / 2, maxScroll))
|
|
65
|
+
if (Math.abs(target - list.scrollTop) < 2) return
|
|
66
|
+
list.scrollTo({ top: target, behavior: 'smooth' })
|
|
67
|
+
// resetKey:鼠标离开导航区域时 bump → effect 重跑一次跟随
|
|
68
|
+
}, [currentKey, markers, resetKey])
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function CodexRail(props: RailProps): ReactNode {
|
|
72
|
+
const { injected, useSessions, t } = props
|
|
73
|
+
const panelRef = useRef<HTMLDivElement>(null)
|
|
74
|
+
const listRef = useRef<HTMLDivElement>(null)
|
|
75
|
+
const [jumpingKey, setJumpingKey] = useState<string | null>(null)
|
|
76
|
+
const [configOpen, setConfigOpen] = useState(false)
|
|
77
|
+
const [searchOpen, setSearchOpen] = useState(false)
|
|
78
|
+
const [favFilter, setFavFilter] = useState(false)
|
|
79
|
+
const [drawerOpen, setDrawerOpen] = useState(false)
|
|
80
|
+
// 鼠标离开导航区域触发一次跟随:仅作 effect 重跑计数
|
|
81
|
+
const [followTick, setFollowTick] = useState(0)
|
|
82
|
+
const { showPeek, hidePeek, peekCls } = usePeekState()
|
|
83
|
+
// 上次 hover 命中的槽 key(同一槽内 mouseover 高频触发不重复开卡)
|
|
84
|
+
const lastHoverKey = useRef<string | null>(null)
|
|
85
|
+
|
|
86
|
+
const mobile = useMobileMode()
|
|
87
|
+
|
|
88
|
+
const { sessionId, visible, markers } = useMarkerData(injected, useSessions)
|
|
89
|
+
const { favorites, toggle: toggleFavorite } = useFavorites(sessionId)
|
|
90
|
+
const settings = useNavSettings(injected)
|
|
91
|
+
const { style, align, bandHeight, cardCount, cardItems, markTone, showPaging } = settings
|
|
92
|
+
useMobileSearchButton(() => setDrawerOpen(true), mobile && markers.length > 0, align, t('search.placeholder'))
|
|
93
|
+
|
|
94
|
+
const effectiveFilter = favFilter && favorites.size > 0
|
|
95
|
+
const displayMarkers = effectiveFilter
|
|
96
|
+
? markers.filter((m) => m.members.some((k) => favorites.has(k)))
|
|
97
|
+
: markers
|
|
98
|
+
|
|
99
|
+
usePinning(panelRef, visible, align, settings.railOffset, sessionId)
|
|
100
|
+
const [currentKey, forceKey] = useReadingSpy(visible, displayMarkers)
|
|
101
|
+
const { scrollable, revealed, syncScroll, pageBy, canPageUp, canPageDown } = useBandPaging(listRef, displayMarkers)
|
|
102
|
+
|
|
103
|
+
const caps = STYLE_CAPABILITIES[style]
|
|
104
|
+
const focusRadius = Math.floor((cardCount - 1) / 2)
|
|
105
|
+
const { focus, openCards, cancelClear, scheduleClear } = useCardPreview(displayMarkers, align, focusRadius, caps.hoverCards)
|
|
106
|
+
|
|
107
|
+
const hint = useFailureNotice(t)
|
|
108
|
+
const loading = useLoadingNotice(t)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
useClosePopoversOnOutside(configOpen, searchOpen, () => {
|
|
112
|
+
setConfigOpen(false)
|
|
113
|
+
setSearchOpen(false)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
useFollowCurrent(listRef, currentKey, displayMarkers, followTick)
|
|
117
|
+
|
|
118
|
+
if (!visible || injected === undefined || markers.length === 0) return null
|
|
119
|
+
|
|
120
|
+
const onJump = (slot: RailMarker): void => {
|
|
121
|
+
if (sessionId === undefined) return
|
|
122
|
+
setJumpingKey(slot.key)
|
|
123
|
+
forceKey(slot.key)
|
|
124
|
+
const currentMarker = currentKey === null ? undefined : displayMarkers.find((m) => m.key === currentKey)
|
|
125
|
+
injected.jump(sessionId, slot.key, slot.seq, currentMarker?.seq)
|
|
126
|
+
window.setTimeout(() => setJumpingKey((k) => (k === slot.key ? null : k)), 600)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (mobile) {
|
|
130
|
+
return (
|
|
131
|
+
<>
|
|
132
|
+
{drawerOpen ? (
|
|
133
|
+
<>
|
|
134
|
+
<div className="mgcn-drawerMask" onClick={() => setDrawerOpen(false)} />
|
|
135
|
+
<div className="mgcn-searchDrawer" role="dialog" aria-label={t('search.placeholder')}>
|
|
136
|
+
<SearchBox
|
|
137
|
+
sessionId={sessionId}
|
|
138
|
+
scopes={settings.searchScopes}
|
|
139
|
+
t={t}
|
|
140
|
+
onJump={(key, seq) => {
|
|
141
|
+
setDrawerOpen(false)
|
|
142
|
+
onJump({ key, seq: seq ?? 0, turn: null, time: Date.now(), texts: [], members: [key] })
|
|
143
|
+
}}
|
|
144
|
+
onClose={() => setDrawerOpen(false)}
|
|
145
|
+
emptyContent={
|
|
146
|
+
<div className="mgcn-drawerList">
|
|
147
|
+
{[...markers].reverse().slice(0, 20).map((m) => (
|
|
148
|
+
<button
|
|
149
|
+
key={m.key}
|
|
150
|
+
type="button"
|
|
151
|
+
className="mgcn-drawerItem"
|
|
152
|
+
onClick={() => { setDrawerOpen(false); onJump(m) }}
|
|
153
|
+
>
|
|
154
|
+
<span className="mgcn-drawerItemTitle">
|
|
155
|
+
<span className="mgcn-drawerItemTurn">{m.turn !== null ? 'Turn ' + m.turn : ''}</span>
|
|
156
|
+
<span className="mgcn-drawerItemTime">{clockText(m.time, Date.now())}</span>
|
|
157
|
+
</span>
|
|
158
|
+
<span className="mgcn-drawerItemText">{m.texts[0] ?? ''}</span>
|
|
159
|
+
</button>
|
|
160
|
+
))}
|
|
161
|
+
</div>
|
|
162
|
+
}
|
|
163
|
+
/>
|
|
164
|
+
</div>
|
|
165
|
+
</>
|
|
166
|
+
) : null}
|
|
167
|
+
</>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const hoverOff = (): void => {
|
|
172
|
+
lastHoverKey.current = null
|
|
173
|
+
scheduleClear()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 事件委托(槽是 memo 纯展示,事件挂在列表容器上):
|
|
177
|
+
// - 用 onMouseOver(真实冒泡,槽间移动每次触发)而非 onMouseEnter
|
|
178
|
+
// (容器级 mouseenter 只在从外部进入时触发一次,槽间移动不会重开卡)
|
|
179
|
+
// - hover 命中槽 → 打开/切换到该槽的悬浮卡(波纹由 CSS :hover 完成)
|
|
180
|
+
// - click 命中槽 → 跳转
|
|
181
|
+
const onListOver = (e: React.MouseEvent): void => {
|
|
182
|
+
const el = e.target as HTMLElement
|
|
183
|
+
const slot = el.closest<HTMLElement>('[data-mega-chat-nav-key]')
|
|
184
|
+
if (slot === null) return
|
|
185
|
+
const key = slot.dataset.megaChatNavKey
|
|
186
|
+
if (key === undefined || key === lastHoverKey.current) return
|
|
187
|
+
const marker = displayMarkers.find((m) => m.key === key)
|
|
188
|
+
if (marker === undefined) return
|
|
189
|
+
lastHoverKey.current = key
|
|
190
|
+
cancelClear()
|
|
191
|
+
openCards(marker, slot)
|
|
192
|
+
}
|
|
193
|
+
const onListClick = (e: React.MouseEvent): void => {
|
|
194
|
+
const el = e.target as HTMLElement
|
|
195
|
+
const slot = el.closest<HTMLElement>('[data-mega-chat-nav-key]')
|
|
196
|
+
if (slot === null) return
|
|
197
|
+
const key = slot.dataset.megaChatNavKey
|
|
198
|
+
const marker = displayMarkers.find((m) => m.key === key)
|
|
199
|
+
if (marker !== undefined) onJump(marker)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<div
|
|
204
|
+
ref={panelRef}
|
|
205
|
+
className={align === 'right' ? 'mgcn-rail mgcn-cxRail mgcn-railRight' : 'mgcn-rail mgcn-cxRail'}
|
|
206
|
+
data-mega-chat-nav="rail"
|
|
207
|
+
data-tone={markTone}
|
|
208
|
+
onMouseEnter={showPeek}
|
|
209
|
+
onMouseLeave={() => { hidePeek(); setFollowTick((n) => n + 1) }} // 离开:触发一次跟随计算
|
|
210
|
+
>
|
|
211
|
+
<div className="mgcn-queue">
|
|
212
|
+
{displayMarkers.length === 0 ? (
|
|
213
|
+
<div className="mgcn-empty">{t('strip.empty')}</div>
|
|
214
|
+
) : (
|
|
215
|
+
<div
|
|
216
|
+
className={'mgcn-list' + (scrollable && showPaging !== 'hide' ? '' : ' mgcn-listNoPage')}
|
|
217
|
+
style={{ '--mgcn-band-height': BAND_HEIGHT_PX.codex[bandHeight] + 'px' } as React.CSSProperties}
|
|
218
|
+
>
|
|
219
|
+
<div className="mgcn-actions">
|
|
220
|
+
{settings.showQuickSettings !== 'hide' ? (
|
|
221
|
+
<span className={'mgcn-gear-wrap mgcn-gear-wrap--' + align + peekCls(settings.showQuickSettings, configOpen)}>
|
|
222
|
+
<button
|
|
223
|
+
type="button"
|
|
224
|
+
className="mgcn-config-gear"
|
|
225
|
+
aria-label={t('config.title')}
|
|
226
|
+
onClick={() => { setConfigOpen((v) => !v); setSearchOpen(false) }}
|
|
227
|
+
>
|
|
228
|
+
<svg className="mgcn-btn-icon" viewBox="0 0 24 24" width="12" height="12" aria-hidden="true">
|
|
229
|
+
<circle cx="12" cy="12" r="3" fill="none" stroke="currentColor" strokeWidth="1.7" />
|
|
230
|
+
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
|
|
231
|
+
</svg>
|
|
232
|
+
</button>
|
|
233
|
+
{configOpen ? (
|
|
234
|
+
<QuickSettings injected={injected} t={t} onClose={() => setConfigOpen(false)} />
|
|
235
|
+
) : null}
|
|
236
|
+
</span>
|
|
237
|
+
) : null}
|
|
238
|
+
{settings.showFavorites !== 'hide' ? (
|
|
239
|
+
<span className={'mgcn-gear-wrap mgcn-gear-wrap--' + align + peekCls(settings.showFavorites, false)}>
|
|
240
|
+
<button
|
|
241
|
+
type="button"
|
|
242
|
+
className={effectiveFilter ? 'mgcn-config-gear mgcn-favFilterOn' : 'mgcn-config-gear'}
|
|
243
|
+
aria-label={t('fav.filter')}
|
|
244
|
+
onClick={() => { if (favorites.size > 0) setFavFilter((v) => !v) }}
|
|
245
|
+
>
|
|
246
|
+
<svg className="mgcn-btn-icon" viewBox="0 0 16 16" width="12" height="12" aria-hidden="true">
|
|
247
|
+
<path d="M8 2.2l1.76 3.56 3.93.57-2.84 2.77.67 3.91L8 11.4l-3.52 1.85.67-3.91-2.84-2.77 3.93-.57z"
|
|
248
|
+
fill={favFilter ? 'currentColor' : 'none'} stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round" />
|
|
249
|
+
</svg>
|
|
250
|
+
</button>
|
|
251
|
+
</span>
|
|
252
|
+
) : null}
|
|
253
|
+
{settings.showSearch !== 'hide' ? (
|
|
254
|
+
<span className={'mgcn-gear-wrap mgcn-gear-wrap--' + align + peekCls(settings.showSearch, searchOpen)}>
|
|
255
|
+
<button
|
|
256
|
+
type="button"
|
|
257
|
+
className="mgcn-config-gear"
|
|
258
|
+
aria-label={t('search.placeholder')}
|
|
259
|
+
onClick={() => { setSearchOpen((v) => !v); setConfigOpen(false) }}
|
|
260
|
+
>
|
|
261
|
+
<svg className="mgcn-btn-icon" viewBox="0 0 16 16" width="12" height="12" aria-hidden="true">
|
|
262
|
+
<circle cx="7" cy="7" r="4.5" fill="none" stroke="currentColor" strokeWidth="1.6" />
|
|
263
|
+
<line x1="10.4" y1="10.4" x2="14" y2="14" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" />
|
|
264
|
+
</svg>
|
|
265
|
+
</button>
|
|
266
|
+
{searchOpen ? (
|
|
267
|
+
<SearchBox
|
|
268
|
+
sessionId={sessionId}
|
|
269
|
+
scopes={settings.searchScopes}
|
|
270
|
+
t={t}
|
|
271
|
+
onJump={(key, seq) => onJump({ key, seq: seq ?? 0, turn: null, time: Date.now(), texts: [], members: [key] })}
|
|
272
|
+
onClose={() => setSearchOpen(false)}
|
|
273
|
+
/>
|
|
274
|
+
) : null}
|
|
275
|
+
</span>
|
|
276
|
+
) : null}
|
|
277
|
+
{settings.showCount !== 'hide' ? (
|
|
278
|
+
<span className={'mgcn-count' + peekCls(settings.showCount, false)}>{displayMarkers.length}</span>
|
|
279
|
+
) : null}
|
|
280
|
+
</div>
|
|
281
|
+
{scrollable && showPaging !== 'hide' ? (
|
|
282
|
+
<div className={'mgcn-pageUp' + peekCls(showPaging, false)}>
|
|
283
|
+
<PagingButton
|
|
284
|
+
dir={-1}
|
|
285
|
+
visible={canPageUp}
|
|
286
|
+
label={t('strip.up')}
|
|
287
|
+
onEnter={cancelClear}
|
|
288
|
+
onLeave={hoverOff}
|
|
289
|
+
onClick={() => pageBy(-1)}
|
|
290
|
+
/>
|
|
291
|
+
</div>
|
|
292
|
+
) : null}
|
|
293
|
+
<div
|
|
294
|
+
ref={listRef}
|
|
295
|
+
className="mgcn-cxList"
|
|
296
|
+
onScroll={syncScroll}
|
|
297
|
+
onMouseLeave={hoverOff}
|
|
298
|
+
onMouseOver={onListOver}
|
|
299
|
+
onClick={onListClick}
|
|
300
|
+
>
|
|
301
|
+
{displayMarkers.map((slot) => {
|
|
302
|
+
const current = currentKey === slot.key
|
|
303
|
+
const focused = focus !== null && focus.key === slot.key
|
|
304
|
+
const reveal = revealed?.get(slot.key)
|
|
305
|
+
return (
|
|
306
|
+
<CodexSlot
|
|
307
|
+
key={slot.key}
|
|
308
|
+
marker={slot}
|
|
309
|
+
current={current}
|
|
310
|
+
focused={focused}
|
|
311
|
+
jumping={jumpingKey === slot.key}
|
|
312
|
+
label={slot.texts[0] ?? ''}
|
|
313
|
+
reveal={reveal}
|
|
314
|
+
/>
|
|
315
|
+
)
|
|
316
|
+
})}
|
|
317
|
+
</div>
|
|
318
|
+
{scrollable && showPaging !== 'hide' ? (
|
|
319
|
+
<div className={'mgcn-pageDown' + peekCls(showPaging, false)}>
|
|
320
|
+
<PagingButton
|
|
321
|
+
dir={1}
|
|
322
|
+
visible={canPageDown}
|
|
323
|
+
label={t('strip.down')}
|
|
324
|
+
onEnter={cancelClear}
|
|
325
|
+
onLeave={hoverOff}
|
|
326
|
+
onClick={() => pageBy(1)}
|
|
327
|
+
/>
|
|
328
|
+
</div>
|
|
329
|
+
) : null}
|
|
330
|
+
</div>
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
{loading !== null ? <Hint text={loading} align={align} position="bottom" loading /> : hint !== null ? <Hint text={hint} align={align} /> : null}
|
|
334
|
+
{focus !== null ? (
|
|
335
|
+
<Cards
|
|
336
|
+
focus={focus}
|
|
337
|
+
align={align}
|
|
338
|
+
cardItems={cardItems}
|
|
339
|
+
favorites={favorites}
|
|
340
|
+
onToggleFavorite={toggleFavorite}
|
|
341
|
+
onJump={onJump}
|
|
342
|
+
onEnter={cancelClear}
|
|
343
|
+
onLeave={hoverOff}
|
|
344
|
+
/>
|
|
345
|
+
) : null}
|
|
346
|
+
</div>
|
|
347
|
+
)
|
|
348
|
+
}
|