dsh-plugin-show-me-data 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +27 -0
- package/README.md +96 -0
- package/cordis.patch.yml +40 -0
- package/docs/01-product-effect.md +178 -0
- package/docs/02-architecture.md +275 -0
- package/docs/03-data-contracts.md +291 -0
- package/docs/04-sources.md +342 -0
- package/docs/05-ui-spec.md +167 -0
- package/docs/06-ai-layer.md +194 -0
- package/docs/07-implementation-plan.md +399 -0
- package/docs/08-test-plan.md +133 -0
- package/docs/09-packaging-install.md +249 -0
- package/docs/10-kickoff-prompt.md +94 -0
- package/docs/11-decisions.md +203 -0
- package/docs/12-runtime-verified.md +115 -0
- package/docs/13-acceptance.md +153 -0
- package/docs/14-progress.md +150 -0
- package/docs/15-publish.md +185 -0
- package/lib/app/ai-deterministic.js +327 -0
- package/lib/app/ai-validate.js +284 -0
- package/lib/app/ai.js +440 -0
- package/lib/app/health.js +77 -0
- package/lib/app/overview.js +349 -0
- package/lib/app/propose-indicator.js +122 -0
- package/lib/app/refresh.js +251 -0
- package/lib/app/series-view.js +195 -0
- package/lib/app/watchlist.js +102 -0
- package/lib/client.js +4322 -0
- package/lib/core/ai/prompts.js +213 -0
- package/lib/core/chart/axis.js +133 -0
- package/lib/core/chart/bar.js +58 -0
- package/lib/core/chart/candle.js +216 -0
- package/lib/core/chart/line.js +186 -0
- package/lib/core/chart/scale.js +132 -0
- package/lib/core/format.js +143 -0
- package/lib/core/indicators/catalog.js +1011 -0
- package/lib/core/indicators/resolve.js +196 -0
- package/lib/core/insight/digest.js +250 -0
- package/lib/core/insight/rank.js +115 -0
- package/lib/core/insight/related.js +90 -0
- package/lib/core/insight/rules.js +417 -0
- package/lib/core/stats/derive.js +123 -0
- package/lib/core/stats/series.js +465 -0
- package/lib/core/time/range.js +242 -0
- package/lib/core/types.js +478 -0
- package/lib/host/ai/discussion.js +559 -0
- package/lib/host/ai/dsh-llm-gateway.js +333 -0
- package/lib/host/config.js +194 -0
- package/lib/host/http/respond.js +165 -0
- package/lib/host/http/routes.js +689 -0
- package/lib/host/index.js +293 -0
- package/lib/host/infra/fs-repos.js +179 -0
- package/lib/host/infra/memory-fallback.js +64 -0
- package/lib/host/tools/define-tool.js +295 -0
- package/lib/host/tools/register.js +431 -0
- package/lib/host.js +7 -0
- package/lib/ports/clock.js +57 -0
- package/lib/ports/snapshot-repo.js +48 -0
- package/lib/sources/eastmoney-macro.js +197 -0
- package/lib/sources/eastmoney-quote.js +201 -0
- package/lib/sources/ecb.js +179 -0
- package/lib/sources/fred.js +207 -0
- package/lib/sources/http.js +136 -0
- package/lib/sources/ohlc.js +36 -0
- package/lib/sources/quote-cascade.js +177 -0
- package/lib/sources/registry.js +153 -0
- package/lib/sources/sina-cn.js +197 -0
- package/lib/sources/sina-us.js +187 -0
- package/lib/sources/tencent.js +158 -0
- package/lib/sources/us-treasury-rates.js +275 -0
- package/lib/sources/us-treasury.js +196 -0
- package/lib/sources/worldbank.js +170 -0
- package/package.json +69 -0
- package/src/app/ai-deterministic.js +327 -0
- package/src/app/ai-validate.js +284 -0
- package/src/app/ai.js +440 -0
- package/src/app/health.js +77 -0
- package/src/app/overview.js +349 -0
- package/src/app/propose-indicator.js +122 -0
- package/src/app/refresh.js +251 -0
- package/src/app/series-view.js +195 -0
- package/src/app/watchlist.js +102 -0
- package/src/client/api.js +323 -0
- package/src/client/components.js +1877 -0
- package/src/client/copy.js +368 -0
- package/src/client/index.js +169 -0
- package/src/client/store.js +219 -0
- package/src/core/ai/prompts.js +213 -0
- package/src/core/chart/axis.js +133 -0
- package/src/core/chart/bar.js +58 -0
- package/src/core/chart/candle.js +216 -0
- package/src/core/chart/line.js +186 -0
- package/src/core/chart/scale.js +132 -0
- package/src/core/format.js +143 -0
- package/src/core/indicators/catalog.js +1011 -0
- package/src/core/indicators/resolve.js +196 -0
- package/src/core/insight/digest.js +250 -0
- package/src/core/insight/rank.js +115 -0
- package/src/core/insight/related.js +90 -0
- package/src/core/insight/rules.js +417 -0
- package/src/core/stats/derive.js +123 -0
- package/src/core/stats/series.js +465 -0
- package/src/core/time/range.js +242 -0
- package/src/core/types.js +478 -0
- package/src/host/ai/discussion.js +559 -0
- package/src/host/ai/dsh-llm-gateway.js +333 -0
- package/src/host/config.js +194 -0
- package/src/host/http/respond.js +165 -0
- package/src/host/http/routes.js +689 -0
- package/src/host/index.js +293 -0
- package/src/host/infra/fs-repos.js +179 -0
- package/src/host/infra/memory-fallback.js +64 -0
- package/src/host/tools/define-tool.js +295 -0
- package/src/host/tools/register.js +431 -0
- package/src/ports/clock.js +57 -0
- package/src/ports/snapshot-repo.js +48 -0
- package/src/sources/eastmoney-macro.js +197 -0
- package/src/sources/eastmoney-quote.js +201 -0
- package/src/sources/ecb.js +179 -0
- package/src/sources/fred.js +207 -0
- package/src/sources/http.js +136 -0
- package/src/sources/ohlc.js +36 -0
- package/src/sources/quote-cascade.js +177 -0
- package/src/sources/registry.js +153 -0
- package/src/sources/sina-cn.js +197 -0
- package/src/sources/sina-us.js +187 -0
- package/src/sources/tencent.js +158 -0
- package/src/sources/us-treasury-rates.js +275 -0
- package/src/sources/us-treasury.js +196 -0
- package/src/sources/worldbank.js +170 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every user-facing string in one place (docs/05 §6), so a test can assert that
|
|
3
|
+
* no raw key or bare English leaks into the panel and so translation is a data
|
|
4
|
+
* change rather than a code change.
|
|
5
|
+
*
|
|
6
|
+
* @module client/copy
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Chinese copy (the panel's primary language). */
|
|
10
|
+
export const COPY = {
|
|
11
|
+
panelTitle: '数据雷达',
|
|
12
|
+
panelSubtitle: '全球宏观 · 利率 · 股市 · 债市',
|
|
13
|
+
open: '打开数据雷达',
|
|
14
|
+
close: '收起面板',
|
|
15
|
+
refresh: '刷新',
|
|
16
|
+
refreshing: '正在刷新…',
|
|
17
|
+
retry: '重试',
|
|
18
|
+
loading: '正在获取数据…',
|
|
19
|
+
empty: '这个范围里没有可用数据。',
|
|
20
|
+
degradedBanner: '部分数据源不可用,卡片显示的是上次成功的快照。',
|
|
21
|
+
unreachable: '插件未正确挂载(/api/show-me-data 不可达)。请检查挂载行与宿主日志。',
|
|
22
|
+
sourceBadge: '来源',
|
|
23
|
+
viewSource: '打开原始数据源',
|
|
24
|
+
showDataTable: '查看数据表',
|
|
25
|
+
hideDataTable: '收起数据表',
|
|
26
|
+
date: '日期',
|
|
27
|
+
value: '数值',
|
|
28
|
+
stats: '统计',
|
|
29
|
+
statsMean: '均值',
|
|
30
|
+
statsMin: '最小',
|
|
31
|
+
statsMax: '最大',
|
|
32
|
+
statsStdDev: '标准差',
|
|
33
|
+
statsPercentile: '分位',
|
|
34
|
+
statsYoy: '同比',
|
|
35
|
+
statsMom: '环比',
|
|
36
|
+
statsMissing: '缺口',
|
|
37
|
+
noteworthy: '今日值得关注',
|
|
38
|
+
noteworthyEmpty: '今天没有触发关注规则。',
|
|
39
|
+
tabToday: '今日',
|
|
40
|
+
tabCore: '核心',
|
|
41
|
+
tabMine: '我的',
|
|
42
|
+
tabSettings: '设置',
|
|
43
|
+
tabDetail: '详情',
|
|
44
|
+
groupUS: '美国',
|
|
45
|
+
groupCN: '中国',
|
|
46
|
+
groupGlobal: '全球',
|
|
47
|
+
groupCustom: '我的',
|
|
48
|
+
ai: 'AI 解析',
|
|
49
|
+
aiSummary: '时段总结',
|
|
50
|
+
aiAsk: '数据问答',
|
|
51
|
+
aiAskPlaceholder: '用面板内的数据提问,例如:美国 CPI 同比现在是多少?',
|
|
52
|
+
aiAskSubmit: '提问',
|
|
53
|
+
aiModeDeterministic: '确定性摘要模式',
|
|
54
|
+
aiModeLlm: '模型模式',
|
|
55
|
+
aiDegraded: 'AI 输出未通过溯源校验,已回退为确定性摘要。',
|
|
56
|
+
aiInsufficient: '数据不足',
|
|
57
|
+
aiCitations: '引用数据点',
|
|
58
|
+
discuss: '开独立会话讨论',
|
|
59
|
+
discussing: '正在开启会话…',
|
|
60
|
+
discussFailed: '独立会话不可用',
|
|
61
|
+
discussTurnFailed: '会话已创建,但本轮提问失败',
|
|
62
|
+
discussHint: '会新建一个 DSH 会话,并把面板数据作为上下文;没输入问题时面板会代你提出开场问题。回答在会话里流式生成,可以继续追问、换模型。',
|
|
63
|
+
discussOpened: '已在新会话中打开(回答正在会话里生成,稍后也会显示在这里)。',
|
|
64
|
+
discussAnswer: '会话里的回答',
|
|
65
|
+
discussOpening: '正在开启会话…',
|
|
66
|
+
discussRunning: '会话进行中',
|
|
67
|
+
discussReady: '会话已就绪',
|
|
68
|
+
discussRunningHint: '回答在会话里生成,完成后也会显示在这里(通常 30–60 秒)。',
|
|
69
|
+
discussOpenSession: '打开会话',
|
|
70
|
+
discussSessionStarted: '会话 ID:',
|
|
71
|
+
discussTimeout: '等待超时:会话仍在运行,请点击「打开会话」查看;或稍后重试。',
|
|
72
|
+
dismiss: '收起',
|
|
73
|
+
noteworthyDiscuss: '讨论',
|
|
74
|
+
noteworthyDiscussHint: '开一个独立会话,把这条的关注原因和面板数据一起带过去',
|
|
75
|
+
analyzeTab: '总体分析',
|
|
76
|
+
groupUs: '美国',
|
|
77
|
+
groupCn: '中国',
|
|
78
|
+
analyzeScope: '范围',
|
|
79
|
+
analyzeAll: '全部',
|
|
80
|
+
analyzeRun: '生成总体分析',
|
|
81
|
+
analyzeIdle: '选择范围后点「生成总体分析」:会综合该范围内所有指标与今日触发规则。',
|
|
82
|
+
analyzeEmpty: '该范围内没有可用指标。',
|
|
83
|
+
analyzeCount: '纳入指标',
|
|
84
|
+
analyzeDiscuss: '深入讨论',
|
|
85
|
+
analyzeDiscussHint: '带着这份总体数据开一个独立会话,可以继续追问',
|
|
86
|
+
iterateHint: '面板只读取已登记的数据源,无法自己接入新源。可以开一个独立会话,在插件工作区里迭代代码来接入(会话已带上这条需求与失败原因)。',
|
|
87
|
+
iterateAction: '开迭代会话接入数据源',
|
|
88
|
+
iterateOpening: '正在开会话…',
|
|
89
|
+
iterateTopic: '接入数据源',
|
|
90
|
+
viewDetail: '查看',
|
|
91
|
+
selectHint: '勾选后可跨板块统一分析',
|
|
92
|
+
selectedCount: '已选',
|
|
93
|
+
analyzeSelected: '分析选中',
|
|
94
|
+
clearSelection: '清除选择',
|
|
95
|
+
expand: '展开',
|
|
96
|
+
collapse: '收起',
|
|
97
|
+
barSize: '周期',
|
|
98
|
+
barDay: '日K',
|
|
99
|
+
barWeek: '周K',
|
|
100
|
+
barMonth: '月K',
|
|
101
|
+
chartCandle: 'K线',
|
|
102
|
+
chartArea: '面积',
|
|
103
|
+
chartLine: '折线',
|
|
104
|
+
chartBar: '柱状',
|
|
105
|
+
chartHint: '移动指针查看数值',
|
|
106
|
+
pointsUnit: '个观测点',
|
|
107
|
+
discussEmpty: '会话里这一轮没有产生文本(模型可能只给出了推理,或中途失败);请打开该会话查看原因。',
|
|
108
|
+
discussIdle: '会话已就绪,但面板只注入了上下文、没有提问;请打开会话直接提问。',
|
|
109
|
+
discussAutoAsked: '面板已代你提出开场问题:',
|
|
110
|
+
discussQuestion: '本轮提问:',
|
|
111
|
+
aiCached: '来自缓存',
|
|
112
|
+
aiOffline: '未配置模型,本回答由本地规则生成。',
|
|
113
|
+
aiEmptyAnswer: '模型未返回可用的文本,已回退为确定性摘要',
|
|
114
|
+
addIndicator: '添加指标',
|
|
115
|
+
addSearchPlaceholder: '搜索指标(中文或英文,例如 非农 / CPI)',
|
|
116
|
+
addByText: '用自然语言添加',
|
|
117
|
+
addByTextPlaceholder: '例如:加上美国 30 年期房贷利率',
|
|
118
|
+
addConfirm: '加入我的',
|
|
119
|
+
addPreview: '预览',
|
|
120
|
+
addExists: '已存在,是否更新?',
|
|
121
|
+
addUnsupported: '无法添加(没有可用数据源)',
|
|
122
|
+
remove: '移除',
|
|
123
|
+
disclaimer: '数据来自公开源,仅供研究参考,不构成投资建议;口径以原始来源为准。',
|
|
124
|
+
lastUpdated: '更新于',
|
|
125
|
+
fromCache: '正在显示缓存',
|
|
126
|
+
settingsMount: '挂载检查清单',
|
|
127
|
+
settingsWhatIsThis: '这个面板在做什么',
|
|
128
|
+
settingsIntro: '下面是当前挂载生效的配置。多数项来自 profile 的行配置,改完需要重启 dsh 进程;面板内不可改的项已标注。',
|
|
129
|
+
settingsPrefix: '接口前缀',
|
|
130
|
+
settingsPrefixHint: '所有面板请求都走这个前缀',
|
|
131
|
+
settingsRoutes: '路由数',
|
|
132
|
+
settingsTools: '模型工具数',
|
|
133
|
+
settingsReadOnly: '行配置,面板内不可改',
|
|
134
|
+
settingsUnsupported: '暂不可用指标:',
|
|
135
|
+
settingsStorageHint: '未配置时为内存存储,重启后缓存与自选清空',
|
|
136
|
+
settingsAi: 'AI 设置',
|
|
137
|
+
settingsCurrentModel: '当前模型',
|
|
138
|
+
settingsAiEnabled: 'AI 开关',
|
|
139
|
+
settingsAiEnabledHint: '关掉后只用确定性摘要(不调用模型)',
|
|
140
|
+
settingsAiChars: '输出字数上限',
|
|
141
|
+
settingsAiCharsHint: '太小时推理模型会把预算用完、只留下空回答',
|
|
142
|
+
settingsAiCache: '结果缓存',
|
|
143
|
+
settingsAiCacheHint: '同一指标+同一区间在缓存期内直接复用',
|
|
144
|
+
settingsData: '数据与缓存',
|
|
145
|
+
settingsRefresh: '后台刷新间隔',
|
|
146
|
+
settingsRefreshHint: '0 表示不自动刷新,只在打开面板时取数',
|
|
147
|
+
settingsGroups: '展示分组',
|
|
148
|
+
settingsGroupsHint: '面板按这些分组归类卡片',
|
|
149
|
+
settingsNoteworthy: '关注项数量',
|
|
150
|
+
settingsNoteworthyHint: '「今日值得关注」最多显示几条',
|
|
151
|
+
settingsDebug: '调试日志',
|
|
152
|
+
settingsDebugHint: '开启后把 AI 诊断写到进程 stderr',
|
|
153
|
+
settingsSourcesHint: '可用性来自最近一次抓取;失败通常是上游限流或封锁。作为备用上游、从未被请求过的源会显示"未检查"——那不代表它坏了。',
|
|
154
|
+
settingsSourceOff: '已在配置中关闭',
|
|
155
|
+
settingsLast: '最后成功',
|
|
156
|
+
settingsHowToChange: '要修改以上任一项:编辑',
|
|
157
|
+
reload: '重新读取',
|
|
158
|
+
testSource: '测试连接',
|
|
159
|
+
testSourceHint: '忽略缓存重新请求该数据源的指标,确认上游此刻是否可用',
|
|
160
|
+
testAll: '全部测试',
|
|
161
|
+
testing: '测试中…',
|
|
162
|
+
testOk: '连接正常',
|
|
163
|
+
testFailed: '连接失败',
|
|
164
|
+
failedCount: '失败',
|
|
165
|
+
probeIndicators: '本次测试指标数',
|
|
166
|
+
notChecked: '未检查',
|
|
167
|
+
available: '可用',
|
|
168
|
+
unavailable: '失败',
|
|
169
|
+
yes: '是',
|
|
170
|
+
no: '否',
|
|
171
|
+
minutes: '分钟',
|
|
172
|
+
settingsStorage: '存储目录',
|
|
173
|
+
settingsAiMode: 'AI 模式',
|
|
174
|
+
settingsSources: '数据源开关',
|
|
175
|
+
settingsIndicators: '指标数量',
|
|
176
|
+
coldStart: '正在获取数据源数据…',
|
|
177
|
+
staleHint: '按该指标的发布频率判断,数据可能尚未更新。',
|
|
178
|
+
errorHint: '该数据源本次取数失败。点击重试,或查看健康状态。',
|
|
179
|
+
missingHint: '上游成功返回,但本范围内没有观测。',
|
|
180
|
+
hoverHint: '悬停查看数值,点击打开详情。',
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** English mirror, used when the locale service reports a non-Chinese locale. */
|
|
184
|
+
export const COPY_EN = {
|
|
185
|
+
panelTitle: 'Data Radar',
|
|
186
|
+
panelSubtitle: 'Global macro · rates · equities · bonds',
|
|
187
|
+
open: 'Open Data Radar',
|
|
188
|
+
close: 'Collapse panel',
|
|
189
|
+
refresh: 'Refresh',
|
|
190
|
+
refreshing: 'Refreshing…',
|
|
191
|
+
retry: 'Retry',
|
|
192
|
+
loading: 'Fetching data…',
|
|
193
|
+
empty: 'No data available in this range.',
|
|
194
|
+
degradedBanner: 'Some sources are unavailable; cards show the last successful snapshot.',
|
|
195
|
+
unreachable: 'The plugin is not mounted correctly (/api/show-me-data is unreachable).',
|
|
196
|
+
sourceBadge: 'Source',
|
|
197
|
+
viewSource: 'Open the original source',
|
|
198
|
+
showDataTable: 'Show data table',
|
|
199
|
+
hideDataTable: 'Hide data table',
|
|
200
|
+
date: 'Date',
|
|
201
|
+
value: 'Value',
|
|
202
|
+
stats: 'Statistics',
|
|
203
|
+
statsMean: 'Mean',
|
|
204
|
+
statsMin: 'Min',
|
|
205
|
+
statsMax: 'Max',
|
|
206
|
+
statsStdDev: 'Std dev',
|
|
207
|
+
statsPercentile: 'Percentile',
|
|
208
|
+
statsYoy: 'YoY',
|
|
209
|
+
statsMom: 'MoM',
|
|
210
|
+
statsMissing: 'Gaps',
|
|
211
|
+
noteworthy: 'Worth watching today',
|
|
212
|
+
noteworthyEmpty: 'No rule fired today.',
|
|
213
|
+
tabToday: 'Today',
|
|
214
|
+
tabCore: 'Core',
|
|
215
|
+
tabMine: 'Mine',
|
|
216
|
+
tabSettings: 'Settings',
|
|
217
|
+
tabDetail: 'Detail',
|
|
218
|
+
groupUS: 'United States',
|
|
219
|
+
groupCN: 'China',
|
|
220
|
+
groupGlobal: 'Global',
|
|
221
|
+
groupCustom: 'Mine',
|
|
222
|
+
ai: 'AI analysis',
|
|
223
|
+
aiSummary: 'Period summary',
|
|
224
|
+
aiAsk: 'Ask the data',
|
|
225
|
+
aiAskPlaceholder: 'Ask using panel data, e.g. what is US CPI YoY now?',
|
|
226
|
+
aiAskSubmit: 'Ask',
|
|
227
|
+
aiModeDeterministic: 'Deterministic summary mode',
|
|
228
|
+
aiModeLlm: 'Model mode',
|
|
229
|
+
aiDegraded: 'The model output failed provenance validation; fell back to the deterministic summary.',
|
|
230
|
+
aiInsufficient: 'Insufficient data',
|
|
231
|
+
aiCitations: 'Cited data points',
|
|
232
|
+
discuss: 'Discuss in a session',
|
|
233
|
+
discussing: 'Opening a session…',
|
|
234
|
+
discussFailed: 'Independent sessions are unavailable',
|
|
235
|
+
discussTurnFailed: 'Session created, this turn failed',
|
|
236
|
+
discussHint: 'Creates a DSH session seeded with the panel data; with an empty question the panel asks an opening question for you. The answer streams in the session.',
|
|
237
|
+
discussOpened: 'Opened in a new session (the answer is generating there and will also appear here).',
|
|
238
|
+
discussAnswer: 'Answer from the session',
|
|
239
|
+
discussOpening: 'Opening a session…',
|
|
240
|
+
discussRunning: 'Session running',
|
|
241
|
+
discussReady: 'Session ready',
|
|
242
|
+
discussRunningHint: 'The answer is generating in the session and will appear here too (usually 30–60s).',
|
|
243
|
+
discussOpenSession: 'Open session',
|
|
244
|
+
discussSessionStarted: 'Session id: ',
|
|
245
|
+
discussTimeout: 'Timed out waiting: the session is still running — open it, or try again later.',
|
|
246
|
+
dismiss: 'Dismiss',
|
|
247
|
+
noteworthyDiscuss: 'Discuss',
|
|
248
|
+
noteworthyDiscussHint: 'Open a session seeded with this item\'s rule triggers and the panel data',
|
|
249
|
+
analyzeTab: 'Analysis',
|
|
250
|
+
groupUs: 'US',
|
|
251
|
+
groupCn: 'China',
|
|
252
|
+
analyzeScope: 'Scope',
|
|
253
|
+
analyzeAll: 'All',
|
|
254
|
+
analyzeRun: 'Run the analysis',
|
|
255
|
+
analyzeIdle: 'Pick a scope and run it: every indicator in scope plus today\'s triggered rules go into one digest.',
|
|
256
|
+
analyzeEmpty: 'No usable indicators in this scope.',
|
|
257
|
+
analyzeCount: 'Indicators in scope',
|
|
258
|
+
analyzeDiscuss: 'Discuss in a session',
|
|
259
|
+
analyzeDiscussHint: 'Open a session seeded with this whole-panel digest',
|
|
260
|
+
iterateHint: 'The panel only reads registered sources and cannot add one by itself. Open a session in the plugin workspace to add it (the request and the reasons travel with it).',
|
|
261
|
+
iterateAction: 'Open an iteration session',
|
|
262
|
+
iterateOpening: 'Opening a session…',
|
|
263
|
+
iterateTopic: 'Add a data source',
|
|
264
|
+
viewDetail: 'View',
|
|
265
|
+
selectHint: 'pick indicators to analyse them together',
|
|
266
|
+
selectedCount: 'Selected',
|
|
267
|
+
analyzeSelected: 'Analyse selection',
|
|
268
|
+
clearSelection: 'Clear',
|
|
269
|
+
expand: 'Expand',
|
|
270
|
+
collapse: 'Collapse',
|
|
271
|
+
barSize: 'Bars',
|
|
272
|
+
barDay: 'Daily',
|
|
273
|
+
barWeek: 'Weekly',
|
|
274
|
+
barMonth: 'Monthly',
|
|
275
|
+
chartCandle: 'Candles',
|
|
276
|
+
chartArea: 'Area',
|
|
277
|
+
chartLine: 'Line',
|
|
278
|
+
chartBar: 'Bars',
|
|
279
|
+
chartHint: 'Move the pointer to read values',
|
|
280
|
+
pointsUnit: 'observations',
|
|
281
|
+
discussEmpty: 'This turn produced no text (the model may have returned only reasoning, or failed midway); open the session to see why.',
|
|
282
|
+
discussIdle: 'The session is ready, but only context was seeded — no question was asked; open it and ask one.',
|
|
283
|
+
discussAutoAsked: 'The panel asked this opening question for you:',
|
|
284
|
+
discussQuestion: 'Question this turn:',
|
|
285
|
+
aiCached: 'From cache',
|
|
286
|
+
aiOffline: 'No model configured; this answer was generated by local rules.',
|
|
287
|
+
aiEmptyAnswer: 'The model returned no usable text; fell back to the deterministic summary',
|
|
288
|
+
addIndicator: 'Add indicator',
|
|
289
|
+
addSearchPlaceholder: 'Search indicators (Chinese or English)',
|
|
290
|
+
addByText: 'Add by description',
|
|
291
|
+
addByTextPlaceholder: 'e.g. add the US 30-year mortgage rate',
|
|
292
|
+
addConfirm: 'Add to mine',
|
|
293
|
+
addPreview: 'Preview',
|
|
294
|
+
addExists: 'Already added — update it?',
|
|
295
|
+
addUnsupported: 'Cannot add (no available source)',
|
|
296
|
+
remove: 'Remove',
|
|
297
|
+
disclaimer: 'Data comes from public sources, for research only, not investment advice.',
|
|
298
|
+
lastUpdated: 'Updated',
|
|
299
|
+
fromCache: 'Showing cache',
|
|
300
|
+
settingsMount: 'Mount checklist',
|
|
301
|
+
settingsWhatIsThis: 'What this panel is doing',
|
|
302
|
+
settingsIntro: 'Effective configuration of this mount. Most values come from the profile row and need a dsh restart; rows the panel cannot change say so.',
|
|
303
|
+
settingsPrefix: 'API prefix',
|
|
304
|
+
settingsPrefixHint: 'every panel request goes through this prefix',
|
|
305
|
+
settingsRoutes: 'Routes',
|
|
306
|
+
settingsTools: 'Model tools',
|
|
307
|
+
settingsReadOnly: 'row config, not editable here',
|
|
308
|
+
settingsUnsupported: 'Unavailable indicators:',
|
|
309
|
+
settingsStorageHint: 'memory when unset: cache and watchlist are cleared on restart',
|
|
310
|
+
settingsAi: 'AI',
|
|
311
|
+
settingsCurrentModel: 'Current model',
|
|
312
|
+
settingsAiEnabled: 'AI enabled',
|
|
313
|
+
settingsAiEnabledHint: 'off means deterministic summaries only',
|
|
314
|
+
settingsAiChars: 'Answer budget',
|
|
315
|
+
settingsAiCharsHint: 'too small and a reasoning model spends it all thinking',
|
|
316
|
+
settingsAiCache: 'Result cache',
|
|
317
|
+
settingsAiCacheHint: 'same indicator and range reuses the cached answer',
|
|
318
|
+
settingsData: 'Data and caching',
|
|
319
|
+
settingsRefresh: 'Background refresh',
|
|
320
|
+
settingsRefreshHint: '0 disables it: data is fetched when the panel opens',
|
|
321
|
+
settingsGroups: 'Display groups',
|
|
322
|
+
settingsGroupsHint: 'cards are grouped by these',
|
|
323
|
+
settingsNoteworthy: 'Noteworthy limit',
|
|
324
|
+
settingsNoteworthyHint: 'rows shown in "worth watching today"',
|
|
325
|
+
settingsDebug: 'Debug logging',
|
|
326
|
+
settingsDebugHint: 'writes AI diagnostics to the process stderr',
|
|
327
|
+
settingsSourcesHint: 'Availability comes from the last fetch; failures are usually upstream rate limits. A source only ever used as a fallback shows "not checked" — that is not a failure.',
|
|
328
|
+
settingsSourceOff: 'disabled in config',
|
|
329
|
+
settingsLast: 'last success',
|
|
330
|
+
settingsHowToChange: 'To change any of these, edit',
|
|
331
|
+
reload: 'Reload',
|
|
332
|
+
testSource: 'Test',
|
|
333
|
+
testSourceHint: 'Re-fetch this source\'s indicators, ignoring the cache, to see whether the upstream answers now',
|
|
334
|
+
testAll: 'Test all',
|
|
335
|
+
testing: 'Testing…',
|
|
336
|
+
testOk: 'reachable',
|
|
337
|
+
testFailed: 'unreachable',
|
|
338
|
+
failedCount: 'failed',
|
|
339
|
+
probeIndicators: 'indicators probed',
|
|
340
|
+
notChecked: 'not checked',
|
|
341
|
+
available: 'ok',
|
|
342
|
+
unavailable: 'failed',
|
|
343
|
+
yes: 'yes',
|
|
344
|
+
no: 'no',
|
|
345
|
+
minutes: 'min',
|
|
346
|
+
settingsStorage: 'Storage directory',
|
|
347
|
+
settingsAiMode: 'AI mode',
|
|
348
|
+
settingsSources: 'Source switches',
|
|
349
|
+
settingsIndicators: 'Indicator count',
|
|
350
|
+
coldStart: 'Fetching from data sources…',
|
|
351
|
+
staleHint: 'This indicator may not have been published yet for its frequency.',
|
|
352
|
+
errorHint: 'This source failed on this refresh. Retry, or check health.',
|
|
353
|
+
missingHint: 'The upstream answered, but has no observation in this range.',
|
|
354
|
+
hoverHint: 'Hover for values, click for detail.',
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** Group key → copy key. */
|
|
358
|
+
export const GROUP_LABELS = { US: 'groupUS', CN: 'groupCN', GLOBAL: 'groupGlobal', CUSTOM: 'groupCustom' }
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Pick the copy table for a locale id.
|
|
362
|
+
*
|
|
363
|
+
* @param {string} [locale] - locale id such as 'zh-CN' or 'en'.
|
|
364
|
+
* @returns {typeof COPY} copy table.
|
|
365
|
+
*/
|
|
366
|
+
export function copyFor(locale) {
|
|
367
|
+
return typeof locale === 'string' && /^en/i.test(locale) ? COPY_EN : COPY
|
|
368
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-half entry (docs/09 §3).
|
|
3
|
+
*
|
|
4
|
+
* Registers one 'shell.overlay' entry that manages its own open/close state, so
|
|
5
|
+
* the plugin never competes with the shipped overlay occupants. Every side effect
|
|
6
|
+
* (stylesheet, subscription) is owned by the plugin context and removed on
|
|
7
|
+
* unload.
|
|
8
|
+
*
|
|
9
|
+
* @module client/index
|
|
10
|
+
*/
|
|
11
|
+
import { api } from './api.js'
|
|
12
|
+
import { copyFor } from './copy.js'
|
|
13
|
+
import { applyIndicatorChange, createStore } from './store.js'
|
|
14
|
+
import { createComponents, insertStyles } from './components.js'
|
|
15
|
+
|
|
16
|
+
/** Services the client half needs. 'slots' is provided by the shipped runtime. */
|
|
17
|
+
export const inject = ['slots']
|
|
18
|
+
|
|
19
|
+
/** The slot this plugin contributes to. */
|
|
20
|
+
export const SLOT = 'shell.overlay'
|
|
21
|
+
|
|
22
|
+
/** A no-op React stand-in used only for the module-scope component bindings. */
|
|
23
|
+
const PLACEHOLDER_REACT = { createElement: () => null, Fragment: null, useState: (v) => [v, () => {}], useEffect: () => {}, useMemo: (f) => f(), useRef: (v) => ({ current: v }) }
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The shared helpers the components need. The bundler concatenates the real
|
|
27
|
+
* implementations into this scope before this module, so the names resolve at
|
|
28
|
+
* call time; declaring the map keeps the dependency explicit.
|
|
29
|
+
*/
|
|
30
|
+
const SHARED_FORMAT = {
|
|
31
|
+
buildLinePath,
|
|
32
|
+
buildBarRects,
|
|
33
|
+
buildSparklineShape,
|
|
34
|
+
buildXAxis,
|
|
35
|
+
buildYAxis,
|
|
36
|
+
buildZeroLine,
|
|
37
|
+
buildTimeAxis,
|
|
38
|
+
buildCandles,
|
|
39
|
+
buildCrosshair,
|
|
40
|
+
buildReferenceLines,
|
|
41
|
+
nearestIndex,
|
|
42
|
+
valueDomain,
|
|
43
|
+
hasOhlc,
|
|
44
|
+
linearScale,
|
|
45
|
+
niceTicks,
|
|
46
|
+
formatValue,
|
|
47
|
+
formatChange,
|
|
48
|
+
formatAge,
|
|
49
|
+
changeColor,
|
|
50
|
+
statusDot,
|
|
51
|
+
metricTooltip,
|
|
52
|
+
truncate,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Registration id inside the overlay list. */
|
|
56
|
+
export const ENTRY_ID = 'show-me-data'
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Build the module-level store and components once per page.
|
|
60
|
+
*
|
|
61
|
+
* @param {object} React - React instance from the loader.
|
|
62
|
+
* @param {object} format - the shared 'core/format' plus chart helpers.
|
|
63
|
+
* @param {{ locale?: string }} [options] - options.
|
|
64
|
+
* @returns {{ store: object, components: object, copy: object }} panel runtime.
|
|
65
|
+
*/
|
|
66
|
+
export function createPanelRuntime(React, format, options = {}) {
|
|
67
|
+
const store = createStore()
|
|
68
|
+
const copy = copyFor(options.locale)
|
|
69
|
+
const components = createComponents(React, { store, api, copy, format, sessions: options.sessions, applyIndicatorChange })
|
|
70
|
+
return { store, components, copy }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Build the panel's components for one React instance.
|
|
75
|
+
*
|
|
76
|
+
* Exported because the structure tests render components with a React stub; the
|
|
77
|
+
* runtime path calls it with the real React it receives.
|
|
78
|
+
*
|
|
79
|
+
* @param {object} React - React instance.
|
|
80
|
+
* @param {{ store?: object, copy?: object, api?: object, format?: object }} [deps] - overrides.
|
|
81
|
+
* @returns {object} components plus the store and copy table they use.
|
|
82
|
+
*/
|
|
83
|
+
export function initPanel(React, deps = {}) {
|
|
84
|
+
const store = deps.store ?? createStore()
|
|
85
|
+
const copy = deps.copy ?? copyFor('zh')
|
|
86
|
+
const components = createComponents(React, {
|
|
87
|
+
store,
|
|
88
|
+
api: deps.api ?? api,
|
|
89
|
+
copy,
|
|
90
|
+
format: deps.format ?? SHARED_FORMAT,
|
|
91
|
+
sessions: deps.sessions,
|
|
92
|
+
applyIndicatorChange: deps.applyIndicatorChange ?? applyIndicatorChange,
|
|
93
|
+
// Injectable so a test can drive the discussion poll to completion instead of
|
|
94
|
+
// leaving a timer that keeps the process alive.
|
|
95
|
+
pollIntervalMs: deps.pollIntervalMs,
|
|
96
|
+
pollTimeoutMs: deps.pollTimeoutMs,
|
|
97
|
+
})
|
|
98
|
+
// The store helpers ride along so the structure tests can exercise the state
|
|
99
|
+
// transitions (not just the render output) of one React instance.
|
|
100
|
+
return { ...components, store, copy, applyIndicatorChange, INITIAL_STATE }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Components for the shared-format placeholder React, so '__test' has bindings. */
|
|
104
|
+
const PANEL = initPanel(PLACEHOLDER_REACT)
|
|
105
|
+
const {
|
|
106
|
+
DataRadarOverlay,
|
|
107
|
+
MetricCard,
|
|
108
|
+
NoteworthyList,
|
|
109
|
+
Chart,
|
|
110
|
+
StatsStrip,
|
|
111
|
+
AiPanel,
|
|
112
|
+
DetailDrawer,
|
|
113
|
+
SettingsTab,
|
|
114
|
+
AddIndicatorTab,
|
|
115
|
+
Sparkline,
|
|
116
|
+
} = PANEL
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Mount the browser half.
|
|
120
|
+
*
|
|
121
|
+
* @param {object} ctx - client plugin context.
|
|
122
|
+
* @param {object} [deps] - injectable dependencies (tests pass stubs).
|
|
123
|
+
* @param {object} [deps.React] - React instance.
|
|
124
|
+
* @param {object} [deps.format] - formatting helpers.
|
|
125
|
+
* @param {Document} [deps.document] - document (browser only).
|
|
126
|
+
* @returns {void}
|
|
127
|
+
*/
|
|
128
|
+
export function apply(ctx, deps = {}) {
|
|
129
|
+
const React = deps.React ?? require('react')
|
|
130
|
+
// The shared core modules are concatenated into this same scope by the
|
|
131
|
+
// bundler, so their symbols are referenced directly rather than re-imported.
|
|
132
|
+
const format = deps.format ?? {
|
|
133
|
+
buildLinePath,
|
|
134
|
+
buildBarRects,
|
|
135
|
+
buildSparklineShape,
|
|
136
|
+
buildXAxis,
|
|
137
|
+
buildYAxis,
|
|
138
|
+
buildZeroLine,
|
|
139
|
+
buildTimeAxis,
|
|
140
|
+
buildCandles,
|
|
141
|
+
buildCrosshair,
|
|
142
|
+
buildReferenceLines,
|
|
143
|
+
nearestIndex,
|
|
144
|
+
valueDomain,
|
|
145
|
+
hasOhlc,
|
|
146
|
+
linearScale,
|
|
147
|
+
niceTicks,
|
|
148
|
+
formatValue,
|
|
149
|
+
formatChange,
|
|
150
|
+
formatAge,
|
|
151
|
+
changeColor,
|
|
152
|
+
statusDot,
|
|
153
|
+
metricTooltip,
|
|
154
|
+
truncate,
|
|
155
|
+
}
|
|
156
|
+
const runtime = createPanelRuntime(React, format, {
|
|
157
|
+
locale: typeof navigator === 'undefined' ? undefined : navigator.language,
|
|
158
|
+
sessions: ctx.get !== undefined ? ctx.get('sessions') : undefined,
|
|
159
|
+
})
|
|
160
|
+
const doc = deps.document ?? (typeof document === 'undefined' ? undefined : document)
|
|
161
|
+
|
|
162
|
+
if (doc !== undefined) {
|
|
163
|
+
ctx.effect(() => insertStyles(doc), 'show-me-data: styles')
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
ctx.slots.inject(SLOT, () =>
|
|
167
|
+
ctx.slots.register({ name: SLOT, id: ENTRY_ID, order: 30 }, runtime.components.DataRadarOverlay),
|
|
168
|
+
)
|
|
169
|
+
}
|