@dshtrading/client-ui-knowledge 0.6.2 → 0.7.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/lib/client/contract.d.ts +1 -1
- package/lib/client/locales.js +36 -2
- package/lib/client.js +1166 -294
- package/lib/client.js.map +1 -1
- package/package.json +5 -5
package/lib/client.js
CHANGED
|
@@ -6,7 +6,8 @@ window.__ModuleLoader__.load({
|
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
-
|
|
9
|
+
//#region ../knowledge/lib/authors.js
|
|
10
|
+
const AUTHOR_ALIASES = Object.freeze({
|
|
10
11
|
"鳄鱼派": "鳄鱼派(像鳄鱼一样思考)",
|
|
11
12
|
"鳄鱼派(公众号:像鳄鱼一样思考)": "鳄鱼派(像鳄鱼一样思考)",
|
|
12
13
|
"像鳄鱼一样思考(鳄鱼派)": "鳄鱼派(像鳄鱼一样思考)",
|
|
@@ -16,6 +17,137 @@ window.__ModuleLoader__.load({
|
|
|
16
17
|
"小鹿投研日记(转播大摩闭门会 9.14,发言人Robin/Steven/钟申/Jenny;Laura股票部分视频截断未录)": "小鹿投研日记",
|
|
17
18
|
"中金点睛(刘刚、杨萱庭)": "中金点睛"
|
|
18
19
|
});
|
|
20
|
+
/** 把作者名归一到规范名;未登记的写法原样返回(仅去除首尾空白)。 */
|
|
21
|
+
function canonicalAuthor(author) {
|
|
22
|
+
const trimmed = author.trim();
|
|
23
|
+
return AUTHOR_ALIASES[trimmed] ?? trimmed;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region ../knowledge/lib/validate.js
|
|
27
|
+
const ALLOWED_CREDIBILITY = /* @__PURE__ */ new Set([
|
|
28
|
+
"high",
|
|
29
|
+
"medium",
|
|
30
|
+
"low"
|
|
31
|
+
]);
|
|
32
|
+
const ALLOWED_SOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
33
|
+
"bilibili",
|
|
34
|
+
"wechat",
|
|
35
|
+
"manual"
|
|
36
|
+
]);
|
|
37
|
+
function isValidSourceUrl(url, type) {
|
|
38
|
+
if (!url || typeof url !== "string") return false;
|
|
39
|
+
const trimmed = url.trim();
|
|
40
|
+
if (type === "manual") return trimmed === "manual" || trimmed.startsWith("manual:") || trimmed.startsWith("http://") || trimmed.startsWith("https://");
|
|
41
|
+
if (type === "bilibili") return trimmed.includes("bilibili.com") || trimmed.includes("b23.tv") || trimmed.startsWith("BV");
|
|
42
|
+
if (type === "wechat") return trimmed.includes("mp.weixin.qq.com") || trimmed.includes("weixin.qq.com");
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
function generateCardId() {
|
|
46
|
+
return `kc_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
|
47
|
+
}
|
|
48
|
+
function validateKnowledgeCard(input, existingCards = []) {
|
|
49
|
+
if (!input || typeof input !== "object") return {
|
|
50
|
+
ok: false,
|
|
51
|
+
error: "输入卡片数据必须是非空对象"
|
|
52
|
+
};
|
|
53
|
+
if (!input.title || typeof input.title !== "string" || input.title.trim().length === 0) return {
|
|
54
|
+
ok: false,
|
|
55
|
+
error: "卡片标题 (title) 为必填字段且不能为空"
|
|
56
|
+
};
|
|
57
|
+
if (!input.summary || typeof input.summary !== "string" || input.summary.trim().length === 0) return {
|
|
58
|
+
ok: false,
|
|
59
|
+
error: "卡片概述 (summary) 为必填字段且不能为空"
|
|
60
|
+
};
|
|
61
|
+
if (!input.source || typeof input.source !== "object") return {
|
|
62
|
+
ok: false,
|
|
63
|
+
error: "卡片来源 (source) 为必填对象"
|
|
64
|
+
};
|
|
65
|
+
const { type, url, author } = input.source;
|
|
66
|
+
if (!type || !ALLOWED_SOURCE_TYPES.has(type)) return {
|
|
67
|
+
ok: false,
|
|
68
|
+
error: `来源类型 (source.type) 非法,允许值为: ${Array.from(ALLOWED_SOURCE_TYPES).join(", ")}`
|
|
69
|
+
};
|
|
70
|
+
if (!url || typeof url !== "string" || url.trim().length === 0) return {
|
|
71
|
+
ok: false,
|
|
72
|
+
error: "来源链接 (source.url) 为必填字段且不能为空"
|
|
73
|
+
};
|
|
74
|
+
if (!isValidSourceUrl(url, type)) return {
|
|
75
|
+
ok: false,
|
|
76
|
+
error: `来源链接 (source.url) 与来源类型 (${type}) 不匹配或不符合白名单规范`
|
|
77
|
+
};
|
|
78
|
+
if (!author || typeof author !== "string" || author.trim().length === 0) return {
|
|
79
|
+
ok: false,
|
|
80
|
+
error: "来源作者/UP主/公众号 (source.author) 为必填字段"
|
|
81
|
+
};
|
|
82
|
+
if (!input.credibility || !ALLOWED_CREDIBILITY.has(input.credibility)) return {
|
|
83
|
+
ok: false,
|
|
84
|
+
error: `内容可信度 (credibility) 非法,允许值为: ${Array.from(ALLOWED_CREDIBILITY).join(", ")}`
|
|
85
|
+
};
|
|
86
|
+
if (!Array.isArray(input.coreClaims) || input.coreClaims.length === 0) return {
|
|
87
|
+
ok: false,
|
|
88
|
+
error: "核心论点 (coreClaims) 必须是非空字符串数组"
|
|
89
|
+
};
|
|
90
|
+
if (!input.factCheck || typeof input.factCheck !== "object") return {
|
|
91
|
+
ok: false,
|
|
92
|
+
error: "事实核查三桶 (factCheck) 为必填对象,包含 verified, discrepancies, unverifiable"
|
|
93
|
+
};
|
|
94
|
+
const { verified = [], discrepancies = [], unverifiable = [] } = input.factCheck;
|
|
95
|
+
if (!Array.isArray(verified) || !Array.isArray(discrepancies) || !Array.isArray(unverifiable)) return {
|
|
96
|
+
ok: false,
|
|
97
|
+
error: "事实核查分类项 (verified / discrepancies / unverifiable) 必须为数组"
|
|
98
|
+
};
|
|
99
|
+
if (!Array.isArray(input.takeaways)) return {
|
|
100
|
+
ok: false,
|
|
101
|
+
error: "可复用经验 (takeaways) 必须为字符串数组"
|
|
102
|
+
};
|
|
103
|
+
if (!Array.isArray(input.boundaries)) return {
|
|
104
|
+
ok: false,
|
|
105
|
+
error: "适用边界与避坑 (boundaries) 必须为字符串数组"
|
|
106
|
+
};
|
|
107
|
+
if (!Array.isArray(input.tags) || input.tags.length === 0) return {
|
|
108
|
+
ok: false,
|
|
109
|
+
error: "主题关键词标签 (tags) 必须包含至少一个标签"
|
|
110
|
+
};
|
|
111
|
+
if (input.related && Array.isArray(input.related) && input.related.length > 0) {
|
|
112
|
+
const existingIds = new Set(existingCards.map((c) => c.id));
|
|
113
|
+
for (const relId of input.related) if (!existingIds.has(relId)) return {
|
|
114
|
+
ok: false,
|
|
115
|
+
error: `显式关联卡片 id [${relId}] 在知识库中不存在(拒绝悬空关联)`
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
119
|
+
const id = input.id && input.id.startsWith("kc_") ? input.id : generateCardId();
|
|
120
|
+
const createdAt = input.createdAt || now;
|
|
121
|
+
const updatedAt = now;
|
|
122
|
+
return {
|
|
123
|
+
ok: true,
|
|
124
|
+
card: {
|
|
125
|
+
id,
|
|
126
|
+
title: input.title.trim(),
|
|
127
|
+
summary: input.summary.trim(),
|
|
128
|
+
source: {
|
|
129
|
+
type: input.source.type,
|
|
130
|
+
url: input.source.url.trim(),
|
|
131
|
+
author: canonicalAuthor(input.source.author),
|
|
132
|
+
publishedAt: input.source.publishedAt
|
|
133
|
+
},
|
|
134
|
+
credibility: input.credibility,
|
|
135
|
+
coreClaims: input.coreClaims.map((c) => String(c).trim()).filter(Boolean),
|
|
136
|
+
factCheck: {
|
|
137
|
+
verified: verified.map((c) => String(c).trim()).filter(Boolean),
|
|
138
|
+
discrepancies: discrepancies.map((c) => String(c).trim()).filter(Boolean),
|
|
139
|
+
unverifiable: unverifiable.map((c) => String(c).trim()).filter(Boolean)
|
|
140
|
+
},
|
|
141
|
+
takeaways: input.takeaways.map((c) => String(c).trim()).filter(Boolean),
|
|
142
|
+
boundaries: input.boundaries.map((c) => String(c).trim()).filter(Boolean),
|
|
143
|
+
tags: input.tags.map((t) => String(t).trim()).filter(Boolean),
|
|
144
|
+
tickers: input.tickers ? input.tickers.map((t) => String(t).trim().toUpperCase()).filter(Boolean) : void 0,
|
|
145
|
+
related: input.related ? Array.from(new Set(input.related)) : void 0,
|
|
146
|
+
createdAt,
|
|
147
|
+
updatedAt
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
19
151
|
//#endregion
|
|
20
152
|
//#region ../knowledge/lib/graph.js
|
|
21
153
|
function buildGraph(cards, options = {}) {
|
|
@@ -161,6 +293,298 @@ window.__ModuleLoader__.load({
|
|
|
161
293
|
};
|
|
162
294
|
}
|
|
163
295
|
//#endregion
|
|
296
|
+
//#region ../knowledge/lib/bundle.js
|
|
297
|
+
/** 单次导入卡片数上限:防止一次导入把库撑到图形/检索不可用的量级。 */
|
|
298
|
+
const KNOWLEDGE_BUNDLE_MAX_CARDS = 2e3;
|
|
299
|
+
/** 单文件字节上限(8 MiB):整个文件要先读进内存再解析,读之前就要有界。 */
|
|
300
|
+
const KNOWLEDGE_BUNDLE_MAX_BYTES = 8388608;
|
|
301
|
+
const BUNDLE_GENERATOR = "dsh-trading";
|
|
302
|
+
const BUNDLE_NOTICE = "知识卡片是第三方观点的结构化转述,不构成投资建议;来源作者与原始链接随卡片保留以便查证。";
|
|
303
|
+
/**
|
|
304
|
+
* 由卡片列表构造 bundle。
|
|
305
|
+
*
|
|
306
|
+
* 不做深拷贝也不做字段裁剪:导出的是库里已有的卡片(store 写入面已归一作者),
|
|
307
|
+
* 这里改字段只会让「导出—导入—再导出」三份内容对不上。
|
|
308
|
+
*/
|
|
309
|
+
function createKnowledgeBundle(cards, options = {}) {
|
|
310
|
+
return {
|
|
311
|
+
meta: {
|
|
312
|
+
schemaVersion: 1,
|
|
313
|
+
exportedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
314
|
+
cardCount: cards.length,
|
|
315
|
+
generator: BUNDLE_GENERATOR,
|
|
316
|
+
notice: BUNDLE_NOTICE
|
|
317
|
+
},
|
|
318
|
+
cards
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
/** 落盘文本:2 空格缩进(人可读、可 diff)+ 收尾换行(符合 POSIX 文本约定)。 */
|
|
322
|
+
function serializeKnowledgeBundle(bundle) {
|
|
323
|
+
return JSON.stringify(bundle, null, 2) + "\n";
|
|
324
|
+
}
|
|
325
|
+
function isRecord(value) {
|
|
326
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* 解析 bundle 文本,逐条判定并把失败原因写成能直接给用户看的一句话。
|
|
330
|
+
*
|
|
331
|
+
* 判定顺序即优先级:先看「有没有内容/是不是 JSON/顶层像不像 bundle」这类
|
|
332
|
+
* 一眼能定性的问题,再进 schemaVersion 与卡数上限,最后才逐卡看必填字段——
|
|
333
|
+
* 用户拿到的第一条错误就是最该先修的那条。
|
|
334
|
+
*/
|
|
335
|
+
function parseKnowledgeBundle(text) {
|
|
336
|
+
if (typeof text !== "string" || text.trim().length === 0) return {
|
|
337
|
+
ok: false,
|
|
338
|
+
error: "bundle 内容为空"
|
|
339
|
+
};
|
|
340
|
+
const bytes = new TextEncoder().encode(text).length;
|
|
341
|
+
if (bytes > 8388608) return {
|
|
342
|
+
ok: false,
|
|
343
|
+
error: `bundle 体积 ${bytes} 字节超过上限 ${KNOWLEDGE_BUNDLE_MAX_BYTES} 字节(8 MiB),请拆分后再导入`
|
|
344
|
+
};
|
|
345
|
+
let parsed;
|
|
346
|
+
try {
|
|
347
|
+
parsed = JSON.parse(text);
|
|
348
|
+
} catch (error) {
|
|
349
|
+
return {
|
|
350
|
+
ok: false,
|
|
351
|
+
error: `bundle 不是合法 JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
if (!isRecord(parsed)) return {
|
|
355
|
+
ok: false,
|
|
356
|
+
error: "bundle 顶层必须是 JSON 对象(含 meta 与 cards 两个字段)"
|
|
357
|
+
};
|
|
358
|
+
if (!isRecord(parsed.meta)) return {
|
|
359
|
+
ok: false,
|
|
360
|
+
error: "bundle 缺少 meta 字段(应为对象)"
|
|
361
|
+
};
|
|
362
|
+
if (parsed.cards === void 0) return {
|
|
363
|
+
ok: false,
|
|
364
|
+
error: "bundle 缺少 cards 字段(应为卡片数组)"
|
|
365
|
+
};
|
|
366
|
+
const meta = parsed.meta;
|
|
367
|
+
if (meta.schemaVersion !== 1) return {
|
|
368
|
+
ok: false,
|
|
369
|
+
error: `不支持的 schemaVersion: ${String(meta.schemaVersion)}(本机支持 1)`
|
|
370
|
+
};
|
|
371
|
+
if (!Array.isArray(parsed.cards)) return {
|
|
372
|
+
ok: false,
|
|
373
|
+
error: "bundle 的 cards 必须是数组"
|
|
374
|
+
};
|
|
375
|
+
const cards = parsed.cards;
|
|
376
|
+
if (cards.length > 2e3) return {
|
|
377
|
+
ok: false,
|
|
378
|
+
error: `bundle 卡片数 ${cards.length} 超过上限 ${KNOWLEDGE_BUNDLE_MAX_CARDS} 张,请拆分后再导入`
|
|
379
|
+
};
|
|
380
|
+
for (let index = 0; index < cards.length; index += 1) {
|
|
381
|
+
const card = cards[index];
|
|
382
|
+
const position = `第 ${index + 1} 张卡片(下标 ${index})`;
|
|
383
|
+
if (!isRecord(card)) return {
|
|
384
|
+
ok: false,
|
|
385
|
+
error: `${position}不是 JSON 对象`
|
|
386
|
+
};
|
|
387
|
+
const missing = [];
|
|
388
|
+
if (typeof card.id !== "string" || card.id.trim().length === 0) missing.push("id");
|
|
389
|
+
if (typeof card.title !== "string" || card.title.trim().length === 0) missing.push("title");
|
|
390
|
+
const source = card.source;
|
|
391
|
+
if (!isRecord(source) || typeof source.url !== "string" || source.url.trim().length === 0) missing.push("source.url");
|
|
392
|
+
if (missing.length > 0) return {
|
|
393
|
+
ok: false,
|
|
394
|
+
error: `${position}缺少或非法字段: ${missing.join(", ")}`
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
ok: true,
|
|
399
|
+
bundle: parsed
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* 拆出 related 与其余字段。
|
|
404
|
+
*
|
|
405
|
+
* related 不能简单「置 undefined」:exactOptionalPropertyTypes 下「没有关联」是
|
|
406
|
+
* 不带这个键,而且基础内容若残留 incoming 里已被丢弃的旧引用,校验会替我们放行
|
|
407
|
+
* 一条本该消失的关联。先摘出来、过滤完再条件回填,两个问题一起消掉。
|
|
408
|
+
*/
|
|
409
|
+
function splitRelated(input) {
|
|
410
|
+
const { related, ...base } = input;
|
|
411
|
+
return {
|
|
412
|
+
base,
|
|
413
|
+
related
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* related 存在性校验用的最小占位卡片。
|
|
418
|
+
*
|
|
419
|
+
* 只为凑出「本地 id ∪ 本批次待写 id」这个集合(validateKnowledgeCard 只读 id),
|
|
420
|
+
* 永远不会被写库——所以字段能给多小给多小。
|
|
421
|
+
*/
|
|
422
|
+
function placeholderCard(id) {
|
|
423
|
+
return {
|
|
424
|
+
id,
|
|
425
|
+
title: "",
|
|
426
|
+
summary: "",
|
|
427
|
+
source: {
|
|
428
|
+
type: "manual",
|
|
429
|
+
url: "",
|
|
430
|
+
author: ""
|
|
431
|
+
},
|
|
432
|
+
credibility: "low",
|
|
433
|
+
coreClaims: [],
|
|
434
|
+
factCheck: {
|
|
435
|
+
verified: [],
|
|
436
|
+
discrepancies: [],
|
|
437
|
+
unverifiable: []
|
|
438
|
+
},
|
|
439
|
+
takeaways: [],
|
|
440
|
+
boundaries: [],
|
|
441
|
+
tags: [],
|
|
442
|
+
createdAt: "",
|
|
443
|
+
updatedAt: ""
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* 纯函数:算出「把 incoming 导入本机库」要做的事,不读文件、不调 store、不写库。
|
|
448
|
+
*
|
|
449
|
+
* 去重键是 source.url(与 knowledge-fs.getByUrl 同口径:trim 后精确相等)。
|
|
450
|
+
* 顺序是有意的:先给每张 incoming 定身份与最终 id,再改 related(此时映射表已完整),
|
|
451
|
+
* 最后统一过 validate——任何一张校验失败只进 invalid,不影响其它卡片。
|
|
452
|
+
*/
|
|
453
|
+
function planKnowledgeImport(local, incoming, mode, options = {}) {
|
|
454
|
+
const localIds = new Set(local.map((card) => card.id));
|
|
455
|
+
const localByUrl = /* @__PURE__ */ new Map();
|
|
456
|
+
for (const card of local) {
|
|
457
|
+
const url = typeof card.source?.url === "string" ? card.source.url.trim() : "";
|
|
458
|
+
if (url.length > 0 && !localByUrl.has(url)) localByUrl.set(url, card);
|
|
459
|
+
}
|
|
460
|
+
const skipped = [];
|
|
461
|
+
const remappedIds = [];
|
|
462
|
+
const pending = [];
|
|
463
|
+
/** 本批次已占用的 id:先到的卡片赢,后到的冲突重生成。 */
|
|
464
|
+
const claimedIds = /* @__PURE__ */ new Set();
|
|
465
|
+
/**
|
|
466
|
+
* remappedIds 的 from 侧。这些 bundle id 已经指向别的卡片(本机同链接卡、或本批更早的卡),
|
|
467
|
+
* 同批次里就不能再拿它当新卡 id——否则同一个 id 在映射表里同时指向两张卡,related 解析变含糊。
|
|
468
|
+
*/
|
|
469
|
+
const remappedFrom = /* @__PURE__ */ new Set();
|
|
470
|
+
/** 记录一条 id 重映射:数组给报告与映射表用,集合给「已占用」判定用,两处必须同步。 */
|
|
471
|
+
const recordRemap = (from, to) => {
|
|
472
|
+
remappedIds.push({
|
|
473
|
+
from,
|
|
474
|
+
to
|
|
475
|
+
});
|
|
476
|
+
remappedFrom.add(from);
|
|
477
|
+
};
|
|
478
|
+
for (let index = 0; index < incoming.length; index += 1) {
|
|
479
|
+
const card = incoming[index];
|
|
480
|
+
if (!isRecord(card)) continue;
|
|
481
|
+
const url = typeof card.source?.url === "string" ? card.source.url.trim() : "";
|
|
482
|
+
const existing = url.length > 0 ? localByUrl.get(url) : void 0;
|
|
483
|
+
if (existing) {
|
|
484
|
+
const targetId = existing.id;
|
|
485
|
+
const bundleId = typeof card.id === "string" ? card.id.trim() : "";
|
|
486
|
+
if (bundleId.length > 0 && bundleId !== targetId) recordRemap(bundleId, targetId);
|
|
487
|
+
if (mode === "skip") {
|
|
488
|
+
skipped.push({
|
|
489
|
+
id: targetId,
|
|
490
|
+
title: existing.title,
|
|
491
|
+
url: existing.source.url,
|
|
492
|
+
reason: "source.url 已存在"
|
|
493
|
+
});
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
const id = targetId;
|
|
497
|
+
claimedIds.add(id);
|
|
498
|
+
const merged = {
|
|
499
|
+
...existing,
|
|
500
|
+
...card,
|
|
501
|
+
id,
|
|
502
|
+
createdAt: existing.createdAt
|
|
503
|
+
};
|
|
504
|
+
pending.push({
|
|
505
|
+
kind: "update",
|
|
506
|
+
index,
|
|
507
|
+
id,
|
|
508
|
+
...splitRelated(merged)
|
|
509
|
+
});
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
const bundleId = typeof card.id === "string" ? card.id.trim() : "";
|
|
513
|
+
let id = bundleId.length > 0 && bundleId.startsWith("kc_") ? bundleId : generateCardId();
|
|
514
|
+
if (localIds.has(id) || claimedIds.has(id) || remappedFrom.has(id)) {
|
|
515
|
+
const fresh = generateCardId();
|
|
516
|
+
recordRemap(id, fresh);
|
|
517
|
+
id = fresh;
|
|
518
|
+
}
|
|
519
|
+
claimedIds.add(id);
|
|
520
|
+
const createdAt = typeof card.createdAt === "string" ? card.createdAt : void 0;
|
|
521
|
+
const created = createdAt === void 0 ? {
|
|
522
|
+
...card,
|
|
523
|
+
id
|
|
524
|
+
} : {
|
|
525
|
+
...card,
|
|
526
|
+
id,
|
|
527
|
+
createdAt
|
|
528
|
+
};
|
|
529
|
+
pending.push({
|
|
530
|
+
kind: "create",
|
|
531
|
+
index,
|
|
532
|
+
id,
|
|
533
|
+
...splitRelated(created)
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
const pendingIds = new Set(pending.map((item) => item.id));
|
|
537
|
+
const knownIds = /* @__PURE__ */ new Set([...localIds, ...pendingIds]);
|
|
538
|
+
const remap = new Map(remappedIds.map((entry) => [entry.from, entry.to]));
|
|
539
|
+
const existingForValidation = [...local, ...[...pendingIds].map(placeholderCard)];
|
|
540
|
+
const create = [];
|
|
541
|
+
const update = [];
|
|
542
|
+
const droppedRelated = [];
|
|
543
|
+
const invalid = [];
|
|
544
|
+
const nowIso = options.now?.toISOString();
|
|
545
|
+
for (const item of pending) {
|
|
546
|
+
let related;
|
|
547
|
+
if (Array.isArray(item.related) && item.related.length > 0) {
|
|
548
|
+
const mapped = item.related.map((value) => remap.get(value) ?? value);
|
|
549
|
+
const kept = mapped.filter((value) => knownIds.has(value));
|
|
550
|
+
const missing = Array.from(new Set(mapped.filter((value) => !knownIds.has(value))));
|
|
551
|
+
if (missing.length > 0) droppedRelated.push({
|
|
552
|
+
id: item.id,
|
|
553
|
+
missing
|
|
554
|
+
});
|
|
555
|
+
related = kept.length > 0 ? Array.from(new Set(kept)) : void 0;
|
|
556
|
+
}
|
|
557
|
+
const input = related === void 0 ? item.base : {
|
|
558
|
+
...item.base,
|
|
559
|
+
related
|
|
560
|
+
};
|
|
561
|
+
const validation = validateKnowledgeCard(input, existingForValidation);
|
|
562
|
+
if (!validation.ok || !validation.card) {
|
|
563
|
+
const titleText = typeof input.title === "string" && input.title.trim().length > 0 ? input.title : void 0;
|
|
564
|
+
invalid.push({
|
|
565
|
+
index: item.index,
|
|
566
|
+
error: validation.error ?? "未知校验错误",
|
|
567
|
+
...titleText === void 0 ? {} : { title: titleText }
|
|
568
|
+
});
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
const card = nowIso === void 0 ? validation.card : {
|
|
572
|
+
...validation.card,
|
|
573
|
+
updatedAt: nowIso
|
|
574
|
+
};
|
|
575
|
+
if (item.kind === "create") create.push(card);
|
|
576
|
+
else update.push(card);
|
|
577
|
+
}
|
|
578
|
+
return {
|
|
579
|
+
create,
|
|
580
|
+
update,
|
|
581
|
+
skipped,
|
|
582
|
+
droppedRelated,
|
|
583
|
+
invalid,
|
|
584
|
+
remappedIds
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
//#endregion
|
|
164
588
|
//#region src/client/shell-faces.ts
|
|
165
589
|
/**
|
|
166
590
|
* Shell 面(最小结构镜像):client-ui-knowledge 不 import
|
|
@@ -231,6 +655,51 @@ window.__ModuleLoader__.load({
|
|
|
231
655
|
return stale;
|
|
232
656
|
}
|
|
233
657
|
//#endregion
|
|
658
|
+
//#region src/client/import-export.ts
|
|
659
|
+
/**
|
|
660
|
+
* 知识库导入 / 导出的纯函数面(可 node 单测,不 import react)。
|
|
661
|
+
*
|
|
662
|
+
* 视图与单测共用同一份实现:导出文件名、导出文本、计划摘要、文件文本解析包装。
|
|
663
|
+
* 落盘格式与去重语义全部来自 @dshtrading/knowledge 的 bundle 模块,本模块只负责
|
|
664
|
+
* 「UI 要的那几个数 / 那一段文本」,不复制任何判定逻辑——同一事实只有一个家。
|
|
665
|
+
*/
|
|
666
|
+
/** 两位补零(月份从 0 起、时间各段都可能是一位数)。 */
|
|
667
|
+
function pad2$1(value) {
|
|
668
|
+
return String(value).padStart(2, "0");
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* 导出文件名:'knowledge-YYYYMMDD-HHmmss.json'(本地时间)。
|
|
672
|
+
*
|
|
673
|
+
* 用本地时间而不是 UTC:文件名是给人看、给人找的,用户按自己墙上的时间认文件;
|
|
674
|
+
* 文件内部 meta.exportedAt 才是 UTC 的绝对时刻。
|
|
675
|
+
*/
|
|
676
|
+
function exportKnowledgeFileName(now) {
|
|
677
|
+
return `knowledge-${`${now.getFullYear()}${pad2$1(now.getMonth() + 1)}${pad2$1(now.getDate())}`}-${`${pad2$1(now.getHours())}${pad2$1(now.getMinutes())}${pad2$1(now.getSeconds())}`}.json`;
|
|
678
|
+
}
|
|
679
|
+
/** 导出文本:卡片列表 → bundle → 落盘文本(换行与缩进口径见 serializeKnowledgeBundle)。 */
|
|
680
|
+
function exportKnowledgeText(cards, now) {
|
|
681
|
+
return serializeKnowledgeBundle(createKnowledgeBundle(cards, { now }));
|
|
682
|
+
}
|
|
683
|
+
/** 导入预览要显示的五个计数(预览面板与结果提示共用同一份口径)。 */
|
|
684
|
+
function importPlanSummary(plan) {
|
|
685
|
+
return {
|
|
686
|
+
create: plan.create.length,
|
|
687
|
+
update: plan.update.length,
|
|
688
|
+
skip: plan.skipped.length,
|
|
689
|
+
dropped: plan.droppedRelated.length,
|
|
690
|
+
invalid: plan.invalid.length
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* 解析文件文本(薄包装)。
|
|
695
|
+
*
|
|
696
|
+
* 保持单一解析入口:视图只经这里碰 bundle 文本,将来换解析实现或加护栏时
|
|
697
|
+
* 视图与单测一起跟着走,不会出现「预览用一种读法、写库用另一种读法」。
|
|
698
|
+
*/
|
|
699
|
+
function parseImportFileText(text) {
|
|
700
|
+
return parseKnowledgeBundle(text);
|
|
701
|
+
}
|
|
702
|
+
//#endregion
|
|
234
703
|
//#region src/client/icons.tsx
|
|
235
704
|
/** 放大镜搜索图标。 */
|
|
236
705
|
function IconSearch({ size = 14 }) {
|
|
@@ -10157,7 +10626,7 @@ window.__ModuleLoader__.load({
|
|
|
10157
10626
|
});
|
|
10158
10627
|
//#endregion
|
|
10159
10628
|
//#region \0dsh-css:/home/runner/work/dsh-trading/dsh-trading/packages/client-ui-knowledge/src/client/KnowledgeView.module.css.mjs
|
|
10160
|
-
const css = ".bZAtrq_root{background-color:var(--dsw-futu-bg-surface);width:100%;height:100%;color:var(--dsw-futu-text-primary);user-select:none;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;display:flex;position:relative;overflow:hidden}.bZAtrq_toolbar{background-color:var(--dsw-futu-bg-base);border-bottom:1px solid var(--dsw-futu-border-subtle);z-index:10;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;padding:8px 12px;display:flex}.bZAtrq_filtersGroup{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.bZAtrq_searchBox{background-color:var(--dsw-futu-bg-surface);border:1px solid var(--dsw-futu-border-subtle);min-width:220px;color:var(--dsw-futu-text-primary);border-radius:4px;align-items:center;gap:6px;padding:4px 8px;display:flex}.bZAtrq_searchBox:focus-within{border-color:var(--dsw-futu-border-active);box-shadow:0 0 0 1px var(--dsw-futu-border-active)}.bZAtrq_searchIcon{color:var(--dsw-futu-text-muted);align-items:center;display:flex}.bZAtrq_searchInput{color:var(--dsw-futu-text-primary);background:0 0;border:none;outline:none;width:100%;font-size:12px}.bZAtrq_searchInput::placeholder{color:var(--dsw-futu-text-muted)}.bZAtrq_filterSelect{border:1px solid var(--dsw-futu-border-subtle);background-color:var(--dsw-futu-bg-surface);color:var(--dsw-futu-text-primary);cursor:pointer;border-radius:4px;outline:none;padding:4px 8px;font-size:12px}.bZAtrq_filterSelect:focus{border-color:var(--dsw-futu-border-active)}.bZAtrq_resetBtn{border:1px solid var(--dsw-futu-border-subtle);color:var(--dsw-futu-text-secondary);cursor:pointer;background-color:#0000;border-radius:4px;padding:4px 8px;font-size:11px}.bZAtrq_resetBtn:hover{background-color:var(--dsw-futu-bg-surface-hover);color:var(--dsw-futu-text-primary)}.bZAtrq_statsText{color:var(--dsw-futu-text-muted);font-size:11px;font-family:var(--dsw-futu-font-mono);white-space:nowrap}.bZAtrq_mainArea{flex:1;width:100%;height:100%;position:relative;overflow:hidden}.bZAtrq_graphCanvas{cursor:grab;width:100%;height:100%}.bZAtrq_graphCanvas:active{cursor:grabbing}.bZAtrq_emptyState{height:100%;color:var(--dsw-futu-text-muted);text-align:center;flex-direction:column;justify-content:center;align-items:center;gap:12px;padding:32px;display:flex}.bZAtrq_emptyIcon{color:var(--dsw-futu-text-muted);opacity:.7}.bZAtrq_emptyHint{max-width:360px;font-size:13px;line-height:1.6}.bZAtrq_drawer{background-color:var(--dsw-futu-bg-base);border-left:1px solid var(--dsw-futu-border-default);z-index:50;user-select:text;flex-direction:column;width:380px;max-width:85%;animation:.2s ease-out bZAtrq_slideIn;display:flex;position:absolute;top:0;bottom:0;right:0;box-shadow:-4px 0 16px #00000014}@keyframes bZAtrq_slideIn{0%{transform:translate(100%)}to{transform:translate(0)}}.bZAtrq_drawerHeader{border-bottom:1px solid var(--dsw-futu-border-subtle);background-color:var(--dsw-futu-bg-surface);justify-content:space-between;align-items:flex-start;gap:12px;padding:14px 16px;display:flex}.bZAtrq_drawerTitleGroup{flex:1}.bZAtrq_drawerTitle{color:var(--dsw-futu-text-primary);margin-bottom:6px;font-size:14px;font-weight:600;line-height:1.4}.bZAtrq_metaRow{color:var(--dsw-futu-text-muted);flex-wrap:wrap;align-items:center;gap:8px;font-size:11px;display:flex}.bZAtrq_badge{text-transform:uppercase;border-radius:3px;align-items:center;padding:2px 6px;font-size:10px;font-weight:500;display:inline-flex}.bZAtrq_badgeHigh{color:var(--dsw-futu-down);background-color:#2ba4711f}.bZAtrq_badgeMedium{color:#b88200;background-color:#f6bd1626}.bZAtrq_badgeLow{color:var(--dsw-futu-up);background-color:#e645451f}.bZAtrq_badgePlatform{color:#2563eb;background-color:#3b82f61a}.bZAtrq_closeBtn{color:var(--dsw-futu-text-muted);cursor:pointer;background:0 0;border:none;border-radius:4px;padding:2px 6px;font-size:16px}.bZAtrq_closeBtn:hover{background-color:var(--dsw-futu-bg-surface-hover);color:var(--dsw-futu-text-primary)}.bZAtrq_drawerBody{flex-direction:column;flex:1;gap:16px;padding:16px;display:flex;overflow-y:auto}.bZAtrq_section{flex-direction:column;gap:6px;display:flex}.bZAtrq_sectionLabel{color:var(--dsw-futu-text-secondary);font-size:12px;font-weight:600}.bZAtrq_claimsList{color:var(--dsw-futu-text-primary);margin:0;padding-left:18px;font-size:12px;line-height:1.5}.bZAtrq_claimsList li{margin-bottom:4px}.bZAtrq_factBucket{border-radius:4px;margin-bottom:6px;padding:8px 10px;font-size:11px;line-height:1.4}.bZAtrq_factVerified{border-left:3px solid var(--dsw-futu-down);background-color:#2ba4710f}.bZAtrq_factDiscrepancies{background-color:#f6bd1614;border-left:3px solid #f6bd16}.bZAtrq_factUnverifiable{border-left:3px solid var(--dsw-futu-text-muted);background-color:#8e95a314}.bZAtrq_bucketTitle{margin-bottom:4px;font-weight:600}.bZAtrq_bucketList{margin:0;padding-left:14px}.bZAtrq_tagPills{flex-wrap:wrap;gap:6px;display:flex}.bZAtrq_tagPill{background-color:var(--dsw-futu-bg-surface);border:1px solid var(--dsw-futu-border-subtle);color:var(--dsw-futu-text-secondary);border-radius:12px;padding:2px 8px;font-size:11px}.bZAtrq_relatedItem{background-color:var(--dsw-futu-bg-surface);color:#2563eb;cursor:pointer;border:1px solid var(--dsw-futu-border-subtle);border-radius:4px;margin-bottom:4px;padding:4px 8px;font-size:11px}.bZAtrq_relatedItem:hover{background-color:var(--dsw-futu-bg-surface-hover);text-decoration:underline}.bZAtrq_drawerFooter{border-top:1px solid var(--dsw-futu-border-subtle);background-color:var(--dsw-futu-bg-surface);padding:12px 16px}.bZAtrq_openSourceBtn{text-align:center;background-color:var(--dsw-futu-border-active);color:#fff;cursor:pointer;border-radius:4px;width:100%;padding:6px 12px;font-size:12px;font-weight:500;text-decoration:none;display:block}.bZAtrq_openSourceBtn:hover{opacity:.9}";
|
|
10629
|
+
const css = ".bZAtrq_root{background-color:var(--dsw-futu-bg-surface);width:100%;height:100%;color:var(--dsw-futu-text-primary);user-select:none;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;display:flex;position:relative;overflow:hidden}.bZAtrq_toolbar{background-color:var(--dsw-futu-bg-base);border-bottom:1px solid var(--dsw-futu-border-subtle);z-index:10;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;padding:8px 12px;display:flex}.bZAtrq_filtersGroup{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.bZAtrq_searchBox{background-color:var(--dsw-futu-bg-surface);border:1px solid var(--dsw-futu-border-subtle);min-width:220px;color:var(--dsw-futu-text-primary);border-radius:4px;align-items:center;gap:6px;padding:4px 8px;display:flex}.bZAtrq_searchBox:focus-within{border-color:var(--dsw-futu-border-active);box-shadow:0 0 0 1px var(--dsw-futu-border-active)}.bZAtrq_searchIcon{color:var(--dsw-futu-text-muted);align-items:center;display:flex}.bZAtrq_searchInput{color:var(--dsw-futu-text-primary);background:0 0;border:none;outline:none;width:100%;font-size:12px}.bZAtrq_searchInput::placeholder{color:var(--dsw-futu-text-muted)}.bZAtrq_filterSelect{border:1px solid var(--dsw-futu-border-subtle);background-color:var(--dsw-futu-bg-surface);color:var(--dsw-futu-text-primary);cursor:pointer;border-radius:4px;outline:none;padding:4px 8px;font-size:12px}.bZAtrq_filterSelect:focus{border-color:var(--dsw-futu-border-active)}.bZAtrq_resetBtn{border:1px solid var(--dsw-futu-border-subtle);color:var(--dsw-futu-text-secondary);cursor:pointer;background-color:#0000;border-radius:4px;padding:4px 8px;font-size:11px}.bZAtrq_resetBtn:hover{background-color:var(--dsw-futu-bg-surface-hover);color:var(--dsw-futu-text-primary)}.bZAtrq_statsText{color:var(--dsw-futu-text-muted);font-size:11px;font-family:var(--dsw-futu-font-mono);white-space:nowrap}.bZAtrq_mainArea{flex:1;width:100%;height:100%;position:relative;overflow:hidden}.bZAtrq_graphCanvas{cursor:grab;width:100%;height:100%}.bZAtrq_graphCanvas:active{cursor:grabbing}.bZAtrq_emptyState{height:100%;color:var(--dsw-futu-text-muted);text-align:center;flex-direction:column;justify-content:center;align-items:center;gap:12px;padding:32px;display:flex}.bZAtrq_emptyIcon{color:var(--dsw-futu-text-muted);opacity:.7}.bZAtrq_emptyHint{max-width:360px;font-size:13px;line-height:1.6}.bZAtrq_drawer{background-color:var(--dsw-futu-bg-base);border-left:1px solid var(--dsw-futu-border-default);z-index:50;user-select:text;flex-direction:column;width:380px;max-width:85%;animation:.2s ease-out bZAtrq_slideIn;display:flex;position:absolute;top:0;bottom:0;right:0;box-shadow:-4px 0 16px #00000014}@keyframes bZAtrq_slideIn{0%{transform:translate(100%)}to{transform:translate(0)}}.bZAtrq_drawerHeader{border-bottom:1px solid var(--dsw-futu-border-subtle);background-color:var(--dsw-futu-bg-surface);justify-content:space-between;align-items:flex-start;gap:12px;padding:14px 16px;display:flex}.bZAtrq_drawerTitleGroup{flex:1}.bZAtrq_drawerTitle{color:var(--dsw-futu-text-primary);margin-bottom:6px;font-size:14px;font-weight:600;line-height:1.4}.bZAtrq_metaRow{color:var(--dsw-futu-text-muted);flex-wrap:wrap;align-items:center;gap:8px;font-size:11px;display:flex}.bZAtrq_badge{text-transform:uppercase;border-radius:3px;align-items:center;padding:2px 6px;font-size:10px;font-weight:500;display:inline-flex}.bZAtrq_badgeHigh{color:var(--dsw-futu-down);background-color:#2ba4711f}.bZAtrq_badgeMedium{color:#b88200;background-color:#f6bd1626}.bZAtrq_badgeLow{color:var(--dsw-futu-up);background-color:#e645451f}.bZAtrq_badgePlatform{color:#2563eb;background-color:#3b82f61a}.bZAtrq_closeBtn{color:var(--dsw-futu-text-muted);cursor:pointer;background:0 0;border:none;border-radius:4px;padding:2px 6px;font-size:16px}.bZAtrq_closeBtn:hover{background-color:var(--dsw-futu-bg-surface-hover);color:var(--dsw-futu-text-primary)}.bZAtrq_drawerBody{flex-direction:column;flex:1;gap:16px;padding:16px;display:flex;overflow-y:auto}.bZAtrq_section{flex-direction:column;gap:6px;display:flex}.bZAtrq_sectionLabel{color:var(--dsw-futu-text-secondary);font-size:12px;font-weight:600}.bZAtrq_claimsList{color:var(--dsw-futu-text-primary);margin:0;padding-left:18px;font-size:12px;line-height:1.5}.bZAtrq_claimsList li{margin-bottom:4px}.bZAtrq_factBucket{border-radius:4px;margin-bottom:6px;padding:8px 10px;font-size:11px;line-height:1.4}.bZAtrq_factVerified{border-left:3px solid var(--dsw-futu-down);background-color:#2ba4710f}.bZAtrq_factDiscrepancies{background-color:#f6bd1614;border-left:3px solid #f6bd16}.bZAtrq_factUnverifiable{border-left:3px solid var(--dsw-futu-text-muted);background-color:#8e95a314}.bZAtrq_bucketTitle{margin-bottom:4px;font-weight:600}.bZAtrq_bucketList{margin:0;padding-left:14px}.bZAtrq_tagPills{flex-wrap:wrap;gap:6px;display:flex}.bZAtrq_tagPill{background-color:var(--dsw-futu-bg-surface);border:1px solid var(--dsw-futu-border-subtle);color:var(--dsw-futu-text-secondary);border-radius:12px;padding:2px 8px;font-size:11px}.bZAtrq_relatedItem{background-color:var(--dsw-futu-bg-surface);color:#2563eb;cursor:pointer;border:1px solid var(--dsw-futu-border-subtle);border-radius:4px;margin-bottom:4px;padding:4px 8px;font-size:11px}.bZAtrq_relatedItem:hover{background-color:var(--dsw-futu-bg-surface-hover);text-decoration:underline}.bZAtrq_drawerFooter{border-top:1px solid var(--dsw-futu-border-subtle);background-color:var(--dsw-futu-bg-surface);padding:12px 16px}.bZAtrq_openSourceBtn{text-align:center;background-color:var(--dsw-futu-border-active);color:#fff;cursor:pointer;border-radius:4px;width:100%;padding:6px 12px;font-size:12px;font-weight:500;text-decoration:none;display:block}.bZAtrq_openSourceBtn:hover{opacity:.9}.bZAtrq_ioGroup{align-items:center;gap:8px;display:flex}.bZAtrq_ioBtn{border:1px solid var(--dsw-futu-border-subtle);color:var(--dsw-futu-text-secondary);cursor:pointer;white-space:nowrap;background-color:#0000;border-radius:4px;padding:4px 8px;font-size:11px}.bZAtrq_ioBtn:hover:not(:disabled){background-color:var(--dsw-futu-bg-surface-hover);color:var(--dsw-futu-text-primary)}.bZAtrq_ioBtn:focus-visible{border-color:var(--dsw-futu-border-active);box-shadow:0 0 0 1px var(--dsw-futu-border-active);outline:none}.bZAtrq_ioBtn:disabled{opacity:.45;cursor:not-allowed}.bZAtrq_ioBtnPrimary{background-color:var(--dsw-futu-border-active);border-color:var(--dsw-futu-border-active);color:#fff}.bZAtrq_ioBtnPrimary:hover:not(:disabled){background-color:var(--dsw-futu-border-active);color:#fff;opacity:.9}.bZAtrq_ioFileInput{display:none}.bZAtrq_ioOverlay{z-index:60;pointer-events:none;align-items:flex-start;padding:12px;display:flex;position:absolute;inset:0}.bZAtrq_ioPanel{pointer-events:auto;background-color:var(--dsw-futu-bg-base);border:1px solid var(--dsw-futu-border-default);user-select:text;border-radius:4px;flex-direction:column;width:400px;max-width:100%;max-height:100%;animation:.16s ease-out bZAtrq_ioPanelIn;display:flex;overflow-y:auto;box-shadow:0 4px 16px #0000001f}@keyframes bZAtrq_ioPanelIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.bZAtrq_ioPanelHeader{border-bottom:1px solid var(--dsw-futu-border-subtle);background-color:var(--dsw-futu-bg-surface);justify-content:space-between;align-items:center;gap:12px;padding:10px 12px;display:flex}.bZAtrq_ioPanelTitle{color:var(--dsw-futu-text-primary);font-size:12px;font-weight:600}.bZAtrq_ioPanelBody{flex-direction:column;gap:10px;padding:12px;display:flex}.bZAtrq_ioFileHint{color:var(--dsw-futu-text-muted);word-break:break-all;font-size:11px;line-height:1.5}.bZAtrq_ioModes{flex-direction:column;gap:6px;display:flex}.bZAtrq_ioMode{color:var(--dsw-futu-text-secondary);cursor:pointer;align-items:flex-start;gap:6px;font-size:11px;line-height:1.5;display:flex}.bZAtrq_ioMode input{accent-color:var(--dsw-futu-border-active);flex-shrink:0;margin:2px 0 0}.bZAtrq_ioCounts{grid-template-columns:repeat(5,minmax(0,1fr));gap:6px;display:grid}.bZAtrq_ioCount{background-color:var(--dsw-futu-bg-surface);border:1px solid var(--dsw-futu-border-subtle);border-radius:4px;flex-direction:column;align-items:center;gap:2px;padding:6px 4px;display:flex}.bZAtrq_ioCountValue{color:var(--dsw-futu-text-primary);font-size:13px;font-weight:600;font-family:var(--dsw-futu-font-mono)}.bZAtrq_ioCountLabel{color:var(--dsw-futu-text-muted);white-space:nowrap;font-size:10px}.bZAtrq_ioInvalid{border-left:3px solid var(--dsw-futu-up);background-color:#e645450f;border-radius:4px;flex-direction:column;gap:4px;padding:8px 10px;display:flex}.bZAtrq_ioInvalidTitle{color:var(--dsw-futu-up);font-size:11px;font-weight:600}.bZAtrq_ioInvalidList{color:var(--dsw-futu-text-primary);word-break:break-word;margin:0;padding-left:14px;font-size:11px;line-height:1.5}.bZAtrq_ioNotice{color:var(--dsw-futu-text-primary);word-break:break-word;border-radius:4px;padding:8px 10px;font-size:11px;line-height:1.5}.bZAtrq_ioNoticeError{border-left:3px solid var(--dsw-futu-up);background-color:#e6454514}.bZAtrq_ioNoticeOk{border-left:3px solid var(--dsw-futu-down);background-color:#2ba47114}.bZAtrq_ioPanelFooter{border-top:1px solid var(--dsw-futu-border-subtle);background-color:var(--dsw-futu-bg-surface);justify-content:flex-end;align-items:center;gap:8px;padding:10px 12px;display:flex}.bZAtrq_ioBanner{margin:8px 12px 0}";
|
|
10161
10630
|
const tagId = "@dshtrading/client-ui-knowledge/KnowledgeView.module.css";
|
|
10162
10631
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
10163
10632
|
const tag = document.createElement("style");
|
|
@@ -10167,47 +10636,72 @@ window.__ModuleLoader__.load({
|
|
|
10167
10636
|
document.head.appendChild(tag);
|
|
10168
10637
|
}
|
|
10169
10638
|
var KnowledgeView_module_css_default = {
|
|
10170
|
-
"filterSelect": "bZAtrq_filterSelect",
|
|
10171
|
-
"metaRow": "bZAtrq_metaRow",
|
|
10172
|
-
"badgeMedium": "bZAtrq_badgeMedium",
|
|
10173
|
-
"drawerHeader": "bZAtrq_drawerHeader",
|
|
10174
|
-
"badgeLow": "bZAtrq_badgeLow",
|
|
10175
|
-
"bucketTitle": "bZAtrq_bucketTitle",
|
|
10176
|
-
"root": "bZAtrq_root",
|
|
10177
|
-
"graphCanvas": "bZAtrq_graphCanvas",
|
|
10178
|
-
"badge": "bZAtrq_badge",
|
|
10179
|
-
"searchIcon": "bZAtrq_searchIcon",
|
|
10180
10639
|
"emptyState": "bZAtrq_emptyState",
|
|
10181
|
-
"resetBtn": "bZAtrq_resetBtn",
|
|
10182
|
-
"factUnverifiable": "bZAtrq_factUnverifiable",
|
|
10183
|
-
"badgePlatform": "bZAtrq_badgePlatform",
|
|
10184
|
-
"emptyIcon": "bZAtrq_emptyIcon",
|
|
10185
|
-
"statsText": "bZAtrq_statsText",
|
|
10186
|
-
"factVerified": "bZAtrq_factVerified",
|
|
10187
|
-
"slideIn": "bZAtrq_slideIn",
|
|
10188
|
-
"closeBtn": "bZAtrq_closeBtn",
|
|
10189
|
-
"section": "bZAtrq_section",
|
|
10190
|
-
"openSourceBtn": "bZAtrq_openSourceBtn",
|
|
10191
|
-
"factDiscrepancies": "bZAtrq_factDiscrepancies",
|
|
10192
|
-
"drawerFooter": "bZAtrq_drawerFooter",
|
|
10193
10640
|
"emptyHint": "bZAtrq_emptyHint",
|
|
10194
|
-
"
|
|
10641
|
+
"claimsList": "bZAtrq_claimsList",
|
|
10195
10642
|
"searchInput": "bZAtrq_searchInput",
|
|
10196
|
-
"
|
|
10643
|
+
"metaRow": "bZAtrq_metaRow",
|
|
10644
|
+
"ioBtn": "bZAtrq_ioBtn",
|
|
10645
|
+
"ioCountLabel": "bZAtrq_ioCountLabel",
|
|
10646
|
+
"ioInvalid": "bZAtrq_ioInvalid",
|
|
10647
|
+
"ioFileInput": "bZAtrq_ioFileInput",
|
|
10648
|
+
"bucketTitle": "bZAtrq_bucketTitle",
|
|
10649
|
+
"bucketList": "bZAtrq_bucketList",
|
|
10650
|
+
"ioNotice": "bZAtrq_ioNotice",
|
|
10651
|
+
"ioNoticeError": "bZAtrq_ioNoticeError",
|
|
10652
|
+
"ioPanelHeader": "bZAtrq_ioPanelHeader",
|
|
10197
10653
|
"badgeHigh": "bZAtrq_badgeHigh",
|
|
10198
|
-
"drawerBody": "bZAtrq_drawerBody",
|
|
10199
|
-
"filtersGroup": "bZAtrq_filtersGroup",
|
|
10200
10654
|
"sectionLabel": "bZAtrq_sectionLabel",
|
|
10655
|
+
"ioPanelFooter": "bZAtrq_ioPanelFooter",
|
|
10656
|
+
"openSourceBtn": "bZAtrq_openSourceBtn",
|
|
10657
|
+
"factDiscrepancies": "bZAtrq_factDiscrepancies",
|
|
10658
|
+
"drawerBody": "bZAtrq_drawerBody",
|
|
10201
10659
|
"tagPill": "bZAtrq_tagPill",
|
|
10202
|
-
"
|
|
10660
|
+
"filtersGroup": "bZAtrq_filtersGroup",
|
|
10661
|
+
"ioPanel": "bZAtrq_ioPanel",
|
|
10662
|
+
"ioPanelTitle": "bZAtrq_ioPanelTitle",
|
|
10663
|
+
"ioPanelBody": "bZAtrq_ioPanelBody",
|
|
10664
|
+
"drawerFooter": "bZAtrq_drawerFooter",
|
|
10665
|
+
"badgeMedium": "bZAtrq_badgeMedium",
|
|
10666
|
+
"ioInvalidTitle": "bZAtrq_ioInvalidTitle",
|
|
10667
|
+
"closeBtn": "bZAtrq_closeBtn",
|
|
10668
|
+
"toolbar": "bZAtrq_toolbar",
|
|
10669
|
+
"resetBtn": "bZAtrq_resetBtn",
|
|
10670
|
+
"ioCount": "bZAtrq_ioCount",
|
|
10203
10671
|
"drawer": "bZAtrq_drawer",
|
|
10204
|
-
"
|
|
10672
|
+
"tagPills": "bZAtrq_tagPills",
|
|
10673
|
+
"searchBox": "bZAtrq_searchBox",
|
|
10674
|
+
"factVerified": "bZAtrq_factVerified",
|
|
10675
|
+
"ioInvalidList": "bZAtrq_ioInvalidList",
|
|
10676
|
+
"ioNoticeOk": "bZAtrq_ioNoticeOk",
|
|
10677
|
+
"ioOverlay": "bZAtrq_ioOverlay",
|
|
10678
|
+
"section": "bZAtrq_section",
|
|
10679
|
+
"graphCanvas": "bZAtrq_graphCanvas",
|
|
10680
|
+
"ioCounts": "bZAtrq_ioCounts",
|
|
10681
|
+
"ioCountValue": "bZAtrq_ioCountValue",
|
|
10682
|
+
"ioBtnPrimary": "bZAtrq_ioBtnPrimary",
|
|
10683
|
+
"statsText": "bZAtrq_statsText",
|
|
10205
10684
|
"drawerTitle": "bZAtrq_drawerTitle",
|
|
10685
|
+
"factUnverifiable": "bZAtrq_factUnverifiable",
|
|
10686
|
+
"ioGroup": "bZAtrq_ioGroup",
|
|
10687
|
+
"relatedItem": "bZAtrq_relatedItem",
|
|
10688
|
+
"filterSelect": "bZAtrq_filterSelect",
|
|
10689
|
+
"badge": "bZAtrq_badge",
|
|
10690
|
+
"ioPanelIn": "bZAtrq_ioPanelIn",
|
|
10206
10691
|
"drawerTitleGroup": "bZAtrq_drawerTitleGroup",
|
|
10207
|
-
"
|
|
10208
|
-
"
|
|
10692
|
+
"ioModes": "bZAtrq_ioModes",
|
|
10693
|
+
"badgeLow": "bZAtrq_badgeLow",
|
|
10694
|
+
"ioFileHint": "bZAtrq_ioFileHint",
|
|
10695
|
+
"ioMode": "bZAtrq_ioMode",
|
|
10696
|
+
"ioBanner": "bZAtrq_ioBanner",
|
|
10697
|
+
"slideIn": "bZAtrq_slideIn",
|
|
10698
|
+
"emptyIcon": "bZAtrq_emptyIcon",
|
|
10699
|
+
"root": "bZAtrq_root",
|
|
10700
|
+
"searchIcon": "bZAtrq_searchIcon",
|
|
10701
|
+
"mainArea": "bZAtrq_mainArea",
|
|
10702
|
+
"badgePlatform": "bZAtrq_badgePlatform",
|
|
10209
10703
|
"factBucket": "bZAtrq_factBucket",
|
|
10210
|
-
"
|
|
10704
|
+
"drawerHeader": "bZAtrq_drawerHeader"
|
|
10211
10705
|
};
|
|
10212
10706
|
//#endregion
|
|
10213
10707
|
//#region src/client/KnowledgeView.tsx
|
|
@@ -10219,6 +10713,7 @@ window.__ModuleLoader__.load({
|
|
|
10219
10713
|
* 2. 主画布区(Obsidian 式 force-graph 力导知识图谱网络)
|
|
10220
10714
|
* 3. 右侧详情抽屉(卡片全文:核心论点 + 事实核查三桶 + 复用经验 + 边界避坑 + 来源链接)
|
|
10221
10715
|
* 4. 空态引导与状态持久化 (dshtrading.knowledge.view.v1)
|
|
10716
|
+
* 5. 导出 / 导入动作与导入预览浮动层(非模态:抽屉与图谱照常可交互)
|
|
10222
10717
|
*/
|
|
10223
10718
|
const STORE_KEY = "dshtrading.knowledge.view.v1";
|
|
10224
10719
|
const DEFAULT_STORED = {
|
|
@@ -10240,6 +10735,13 @@ window.__ModuleLoader__.load({
|
|
|
10240
10735
|
const [cards, setCards] = (0, react.useState)([]);
|
|
10241
10736
|
const [selectedCard, setSelectedCard] = (0, react.useState)(null);
|
|
10242
10737
|
const [loading, setLoading] = (0, react.useState)(true);
|
|
10738
|
+
const [draft, setDraft] = (0, react.useState)(null);
|
|
10739
|
+
const [importError, setImportError] = (0, react.useState)(null);
|
|
10740
|
+
const [applied, setApplied] = (0, react.useState)(null);
|
|
10741
|
+
const [importing, setImporting] = (0, react.useState)(false);
|
|
10742
|
+
/** 导出超出导入侧上限(2000 张 / 8 MiB)时的可见提示,下一次导出成功或被缩小筛选后消失。 */
|
|
10743
|
+
const [exportTooLarge, setExportTooLarge] = (0, react.useState)(false);
|
|
10744
|
+
const fileInputRef = (0, react.useRef)(null);
|
|
10243
10745
|
const graphRef = (0, react.useRef)(null);
|
|
10244
10746
|
const bridgeRef = (0, react.useRef)(bridge);
|
|
10245
10747
|
if (bridgeRef.current === null) bridgeRef.current = bridge;
|
|
@@ -10344,6 +10846,11 @@ window.__ModuleLoader__.load({
|
|
|
10344
10846
|
coAuthor: false
|
|
10345
10847
|
});
|
|
10346
10848
|
}, [filteredCards]);
|
|
10849
|
+
const importPlan = (0, react.useMemo)(() => {
|
|
10850
|
+
if (draft === null || draft.bundle === null) return null;
|
|
10851
|
+
return planKnowledgeImport(cards, draft.bundle.cards, draft.mode);
|
|
10852
|
+
}, [draft, cards]);
|
|
10853
|
+
const importCounts = (0, react.useMemo)(() => importPlan === null ? null : importPlanSummary(importPlan), [importPlan]);
|
|
10347
10854
|
const handleResetFilters = () => {
|
|
10348
10855
|
setQuery("");
|
|
10349
10856
|
setSelectedTag("");
|
|
@@ -10352,6 +10859,117 @@ window.__ModuleLoader__.load({
|
|
|
10352
10859
|
setSelectedSourceType("");
|
|
10353
10860
|
};
|
|
10354
10861
|
const hasActiveFilters = Boolean(query || selectedTag || selectedAuthor || selectedCredibility || selectedSourceType);
|
|
10862
|
+
/**
|
|
10863
|
+
* 导出:当前筛选结果(无筛选即全库)直接下载为 JSON 文件,文件名带本地时间戳。
|
|
10864
|
+
*
|
|
10865
|
+
* 导出侧与导入侧同口径(KNOWLEDGE_BUNDLE_MAX_CARDS / KNOWLEDGE_BUNDLE_MAX_BYTES):
|
|
10866
|
+
* 超过上限就一个字节都不下载——否则我们造出的文件自己的导入侧必然拒收,
|
|
10867
|
+
* 用户要到下一次导入才知道白导了一场。
|
|
10868
|
+
*/
|
|
10869
|
+
const handleExport = () => {
|
|
10870
|
+
const now = /* @__PURE__ */ new Date();
|
|
10871
|
+
const text = exportKnowledgeText(filteredCards, now);
|
|
10872
|
+
const bytes = new TextEncoder().encode(text).length;
|
|
10873
|
+
if (filteredCards.length > 2e3 || bytes > 8388608) {
|
|
10874
|
+
setExportTooLarge(true);
|
|
10875
|
+
return;
|
|
10876
|
+
}
|
|
10877
|
+
setExportTooLarge(false);
|
|
10878
|
+
const blob = new Blob([text], { type: "application/json" });
|
|
10879
|
+
const url = URL.createObjectURL(blob);
|
|
10880
|
+
const anchor = document.createElement("a");
|
|
10881
|
+
anchor.href = url;
|
|
10882
|
+
anchor.download = exportKnowledgeFileName(now);
|
|
10883
|
+
anchor.click();
|
|
10884
|
+
URL.revokeObjectURL(url);
|
|
10885
|
+
};
|
|
10886
|
+
const closeImportPanel = (0, react.useCallback)(() => {
|
|
10887
|
+
setDraft(null);
|
|
10888
|
+
setImportError(null);
|
|
10889
|
+
setApplied(null);
|
|
10890
|
+
setImporting(false);
|
|
10891
|
+
const input = fileInputRef.current;
|
|
10892
|
+
if (input !== null) input.value = "";
|
|
10893
|
+
}, []);
|
|
10894
|
+
const handleFileChange = async (event) => {
|
|
10895
|
+
const input = event.currentTarget;
|
|
10896
|
+
const file = input.files?.[0] ?? null;
|
|
10897
|
+
input.value = "";
|
|
10898
|
+
if (file === null) return;
|
|
10899
|
+
setApplied(null);
|
|
10900
|
+
let text;
|
|
10901
|
+
try {
|
|
10902
|
+
text = await file.text();
|
|
10903
|
+
} catch (error) {
|
|
10904
|
+
setImportError(error instanceof Error ? error.message : String(error));
|
|
10905
|
+
setDraft({
|
|
10906
|
+
fileName: file.name,
|
|
10907
|
+
text: "",
|
|
10908
|
+
bundle: null,
|
|
10909
|
+
mode: "skip"
|
|
10910
|
+
});
|
|
10911
|
+
return;
|
|
10912
|
+
}
|
|
10913
|
+
const parsed = parseImportFileText(text);
|
|
10914
|
+
if (!parsed.ok) {
|
|
10915
|
+
setImportError(parsed.error);
|
|
10916
|
+
setDraft({
|
|
10917
|
+
fileName: file.name,
|
|
10918
|
+
text,
|
|
10919
|
+
bundle: null,
|
|
10920
|
+
mode: "skip"
|
|
10921
|
+
});
|
|
10922
|
+
return;
|
|
10923
|
+
}
|
|
10924
|
+
setImportError(null);
|
|
10925
|
+
setDraft({
|
|
10926
|
+
fileName: file.name,
|
|
10927
|
+
text,
|
|
10928
|
+
bundle: parsed.bundle,
|
|
10929
|
+
mode: "skip"
|
|
10930
|
+
});
|
|
10931
|
+
};
|
|
10932
|
+
const handleSelectImportMode = (mode) => {
|
|
10933
|
+
setApplied(null);
|
|
10934
|
+
setImportError(null);
|
|
10935
|
+
setDraft((prev) => prev === null ? prev : {
|
|
10936
|
+
...prev,
|
|
10937
|
+
mode
|
|
10938
|
+
});
|
|
10939
|
+
};
|
|
10940
|
+
const handleConfirmImport = async () => {
|
|
10941
|
+
if (draft === null || draft.bundle === null || importing || applied !== null) return;
|
|
10942
|
+
setImporting(true);
|
|
10943
|
+
setImportError(null);
|
|
10944
|
+
try {
|
|
10945
|
+
const result = await stableBridge.importKnowledgeBundle({
|
|
10946
|
+
mode: draft.mode,
|
|
10947
|
+
bundle: draft.text
|
|
10948
|
+
});
|
|
10949
|
+
if (!result.ok) {
|
|
10950
|
+
setImportError(result.error ?? "");
|
|
10951
|
+
return;
|
|
10952
|
+
}
|
|
10953
|
+
setApplied({
|
|
10954
|
+
created: result.created,
|
|
10955
|
+
updated: result.updated,
|
|
10956
|
+
skipped: result.skipped
|
|
10957
|
+
});
|
|
10958
|
+
await loadCards();
|
|
10959
|
+
} catch (error) {
|
|
10960
|
+
setImportError(error instanceof Error ? error.message : String(error));
|
|
10961
|
+
} finally {
|
|
10962
|
+
setImporting(false);
|
|
10963
|
+
}
|
|
10964
|
+
};
|
|
10965
|
+
(0, react.useEffect)(() => {
|
|
10966
|
+
if (draft === null) return;
|
|
10967
|
+
const onKeyDown = (event) => {
|
|
10968
|
+
if (event.key === "Escape") closeImportPanel();
|
|
10969
|
+
};
|
|
10970
|
+
window.addEventListener("keydown", onKeyDown);
|
|
10971
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
10972
|
+
}, [draft, closeImportPanel]);
|
|
10355
10973
|
const handleJumpToRelated = (relatedId) => {
|
|
10356
10974
|
const target = cards.find((c) => c.id === relatedId);
|
|
10357
10975
|
if (target) {
|
|
@@ -10370,294 +10988,513 @@ window.__ModuleLoader__.load({
|
|
|
10370
10988
|
for (const c of filteredCards) clusters.add(c.tags[0] ?? "untagged");
|
|
10371
10989
|
return clusters.size;
|
|
10372
10990
|
}, [filteredCards]);
|
|
10991
|
+
const exportTitle = filteredCards.length === 0 ? t("kv.io.exportEmpty") : exportTooLarge || filteredCards.length > 2e3 ? t("kv.io.exportTooLarge") : t("kv.io.export");
|
|
10373
10992
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10374
10993
|
className: KnowledgeView_module_css_default.root,
|
|
10375
10994
|
"data-dshtrading-knowledge-view": "",
|
|
10376
|
-
children: [
|
|
10377
|
-
|
|
10378
|
-
|
|
10379
|
-
|
|
10380
|
-
|
|
10381
|
-
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
|
|
10392
|
-
|
|
10393
|
-
}),
|
|
10394
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
10395
|
-
className: KnowledgeView_module_css_default.filterSelect,
|
|
10396
|
-
value: selectedTag,
|
|
10397
|
-
onChange: (e) => setSelectedTag(e.target.value),
|
|
10398
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
10399
|
-
value: "",
|
|
10400
|
-
children: t("kv.filter.allTags")
|
|
10401
|
-
}), allTags.map((tag) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
10402
|
-
value: tag,
|
|
10403
|
-
children: tag
|
|
10404
|
-
}, tag))]
|
|
10405
|
-
}),
|
|
10406
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
10407
|
-
className: KnowledgeView_module_css_default.filterSelect,
|
|
10408
|
-
value: selectedAuthor,
|
|
10409
|
-
onChange: (e) => setSelectedAuthor(e.target.value),
|
|
10410
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
10411
|
-
value: "",
|
|
10412
|
-
children: t("kv.filter.allAuthors")
|
|
10413
|
-
}), allAuthors.map((author) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
10414
|
-
value: author,
|
|
10415
|
-
children: author
|
|
10416
|
-
}, author))]
|
|
10417
|
-
}),
|
|
10418
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
10419
|
-
className: KnowledgeView_module_css_default.filterSelect,
|
|
10420
|
-
value: selectedCredibility,
|
|
10421
|
-
onChange: (e) => setSelectedCredibility(e.target.value),
|
|
10422
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
10423
|
-
value: "",
|
|
10424
|
-
children: t("kv.filter.allCredibility")
|
|
10425
|
-
}), CREDIBILITY_OPTIONS.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
10426
|
-
value: option.value,
|
|
10427
|
-
children: t(option.labelKey)
|
|
10428
|
-
}, option.value))]
|
|
10429
|
-
}),
|
|
10430
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
10431
|
-
className: KnowledgeView_module_css_default.filterSelect,
|
|
10432
|
-
value: selectedSourceType,
|
|
10433
|
-
onChange: (e) => setSelectedSourceType(e.target.value),
|
|
10434
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
10435
|
-
value: "",
|
|
10436
|
-
children: t("kv.filter.allSourceTypes")
|
|
10437
|
-
}), SOURCE_TYPE_OPTIONS.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
10438
|
-
value: option.value,
|
|
10439
|
-
children: t(option.labelKey)
|
|
10440
|
-
}, option.value))]
|
|
10441
|
-
}),
|
|
10442
|
-
hasActiveFilters && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10443
|
-
type: "button",
|
|
10444
|
-
className: KnowledgeView_module_css_default.resetBtn,
|
|
10445
|
-
onClick: handleResetFilters,
|
|
10446
|
-
children: t("kv.filter.reset")
|
|
10447
|
-
})
|
|
10448
|
-
]
|
|
10449
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10450
|
-
className: KnowledgeView_module_css_default.statsText,
|
|
10451
|
-
children: [
|
|
10452
|
-
filteredCards.length,
|
|
10453
|
-
" ",
|
|
10454
|
-
t("kv.stats.cards"),
|
|
10455
|
-
" · ",
|
|
10456
|
-
clusterCount,
|
|
10457
|
-
" ",
|
|
10458
|
-
t("kv.stats.clusters")
|
|
10459
|
-
]
|
|
10460
|
-
})]
|
|
10461
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10462
|
-
className: KnowledgeView_module_css_default.mainArea,
|
|
10463
|
-
children: [cards.length === 0 && !loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10464
|
-
className: KnowledgeView_module_css_default.emptyState,
|
|
10465
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10466
|
-
className: KnowledgeView_module_css_default.emptyIcon,
|
|
10467
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconKnowledge, { size: 40 })
|
|
10468
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10469
|
-
className: KnowledgeView_module_css_default.emptyHint,
|
|
10470
|
-
children: t("kv.empty.hint")
|
|
10471
|
-
})]
|
|
10472
|
-
}) : filteredCards.length === 0 && !loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10473
|
-
className: KnowledgeView_module_css_default.emptyState,
|
|
10474
|
-
children: [
|
|
10475
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10476
|
-
className: KnowledgeView_module_css_default.emptyIcon,
|
|
10477
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconKnowledge, { size: 32 })
|
|
10478
|
-
}),
|
|
10479
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10480
|
-
className: KnowledgeView_module_css_default.emptyHint,
|
|
10481
|
-
children: t("kv.empty.filtered")
|
|
10482
|
-
}),
|
|
10483
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10484
|
-
type: "button",
|
|
10485
|
-
className: KnowledgeView_module_css_default.resetBtn,
|
|
10486
|
-
onClick: handleResetFilters,
|
|
10487
|
-
children: t("kv.filter.reset")
|
|
10488
|
-
})
|
|
10489
|
-
]
|
|
10490
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10491
|
-
className: KnowledgeView_module_css_default.graphCanvas,
|
|
10492
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KnowledgeGraph, {
|
|
10493
|
-
ref: graphRef,
|
|
10494
|
-
data: graphData,
|
|
10495
|
-
selectedCardId: selectedCard?.id,
|
|
10496
|
-
onSelectCard: handleSelectCard,
|
|
10497
|
-
onTagClick: handleTagClick
|
|
10498
|
-
})
|
|
10499
|
-
}), selectedCard && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10500
|
-
className: KnowledgeView_module_css_default.drawer,
|
|
10501
|
-
role: "dialog",
|
|
10502
|
-
"aria-modal": "false",
|
|
10503
|
-
children: [
|
|
10504
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10505
|
-
className: KnowledgeView_module_css_default.drawerHeader,
|
|
10506
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10507
|
-
className: KnowledgeView_module_css_default.drawerTitleGroup,
|
|
10508
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10509
|
-
className: KnowledgeView_module_css_default.drawerTitle,
|
|
10510
|
-
children: selectedCard.title
|
|
10511
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10512
|
-
className: KnowledgeView_module_css_default.metaRow,
|
|
10513
|
-
children: [
|
|
10514
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10515
|
-
className: `${KnowledgeView_module_css_default.badge} ${KnowledgeView_module_css_default.badgePlatform}`,
|
|
10516
|
-
children: selectedCard.source.type
|
|
10517
|
-
}),
|
|
10518
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10519
|
-
className: `${KnowledgeView_module_css_default.badge} ${selectedCard.credibility === "high" ? KnowledgeView_module_css_default.badgeHigh : selectedCard.credibility === "medium" ? KnowledgeView_module_css_default.badgeMedium : KnowledgeView_module_css_default.badgeLow}`,
|
|
10520
|
-
children: selectedCard.credibility
|
|
10521
|
-
}),
|
|
10522
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: selectedCard.source.author }),
|
|
10523
|
-
selectedCard.source.publishedAt && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: selectedCard.source.publishedAt })
|
|
10524
|
-
]
|
|
10995
|
+
children: [
|
|
10996
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10997
|
+
className: KnowledgeView_module_css_default.toolbar,
|
|
10998
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10999
|
+
className: KnowledgeView_module_css_default.filtersGroup,
|
|
11000
|
+
children: [
|
|
11001
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11002
|
+
className: KnowledgeView_module_css_default.searchBox,
|
|
11003
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11004
|
+
className: KnowledgeView_module_css_default.searchIcon,
|
|
11005
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconSearch, { size: 13 })
|
|
11006
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
11007
|
+
type: "text",
|
|
11008
|
+
className: KnowledgeView_module_css_default.searchInput,
|
|
11009
|
+
placeholder: t("kv.search.placeholder"),
|
|
11010
|
+
value: query,
|
|
11011
|
+
onChange: (e) => setQuery(e.target.value)
|
|
10525
11012
|
})]
|
|
10526
|
-
}),
|
|
11013
|
+
}),
|
|
11014
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
11015
|
+
className: KnowledgeView_module_css_default.filterSelect,
|
|
11016
|
+
value: selectedTag,
|
|
11017
|
+
onChange: (e) => setSelectedTag(e.target.value),
|
|
11018
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
11019
|
+
value: "",
|
|
11020
|
+
children: t("kv.filter.allTags")
|
|
11021
|
+
}), allTags.map((tag) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
11022
|
+
value: tag,
|
|
11023
|
+
children: tag
|
|
11024
|
+
}, tag))]
|
|
11025
|
+
}),
|
|
11026
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
11027
|
+
className: KnowledgeView_module_css_default.filterSelect,
|
|
11028
|
+
value: selectedAuthor,
|
|
11029
|
+
onChange: (e) => setSelectedAuthor(e.target.value),
|
|
11030
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
11031
|
+
value: "",
|
|
11032
|
+
children: t("kv.filter.allAuthors")
|
|
11033
|
+
}), allAuthors.map((author) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
11034
|
+
value: author,
|
|
11035
|
+
children: author
|
|
11036
|
+
}, author))]
|
|
11037
|
+
}),
|
|
11038
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
11039
|
+
className: KnowledgeView_module_css_default.filterSelect,
|
|
11040
|
+
value: selectedCredibility,
|
|
11041
|
+
onChange: (e) => setSelectedCredibility(e.target.value),
|
|
11042
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
11043
|
+
value: "",
|
|
11044
|
+
children: t("kv.filter.allCredibility")
|
|
11045
|
+
}), CREDIBILITY_OPTIONS.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
11046
|
+
value: option.value,
|
|
11047
|
+
children: t(option.labelKey)
|
|
11048
|
+
}, option.value))]
|
|
11049
|
+
}),
|
|
11050
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
11051
|
+
className: KnowledgeView_module_css_default.filterSelect,
|
|
11052
|
+
value: selectedSourceType,
|
|
11053
|
+
onChange: (e) => setSelectedSourceType(e.target.value),
|
|
11054
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
11055
|
+
value: "",
|
|
11056
|
+
children: t("kv.filter.allSourceTypes")
|
|
11057
|
+
}), SOURCE_TYPE_OPTIONS.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
11058
|
+
value: option.value,
|
|
11059
|
+
children: t(option.labelKey)
|
|
11060
|
+
}, option.value))]
|
|
11061
|
+
}),
|
|
11062
|
+
hasActiveFilters && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11063
|
+
type: "button",
|
|
11064
|
+
className: KnowledgeView_module_css_default.resetBtn,
|
|
11065
|
+
onClick: handleResetFilters,
|
|
11066
|
+
children: t("kv.filter.reset")
|
|
11067
|
+
})
|
|
11068
|
+
]
|
|
11069
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11070
|
+
className: KnowledgeView_module_css_default.ioGroup,
|
|
11071
|
+
children: [
|
|
11072
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11073
|
+
type: "button",
|
|
11074
|
+
className: KnowledgeView_module_css_default.ioBtn,
|
|
11075
|
+
onClick: handleExport,
|
|
11076
|
+
disabled: filteredCards.length === 0,
|
|
11077
|
+
title: exportTitle,
|
|
11078
|
+
children: t("kv.io.export")
|
|
11079
|
+
}),
|
|
11080
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10527
11081
|
type: "button",
|
|
10528
|
-
className: KnowledgeView_module_css_default.
|
|
10529
|
-
onClick: () =>
|
|
10530
|
-
|
|
10531
|
-
|
|
11082
|
+
className: KnowledgeView_module_css_default.ioBtn,
|
|
11083
|
+
onClick: () => fileInputRef.current?.click(),
|
|
11084
|
+
children: t("kv.io.import")
|
|
11085
|
+
}),
|
|
11086
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
11087
|
+
ref: fileInputRef,
|
|
11088
|
+
type: "file",
|
|
11089
|
+
accept: "application/json,.json",
|
|
11090
|
+
className: KnowledgeView_module_css_default.ioFileInput,
|
|
11091
|
+
"aria-label": t("kv.io.import"),
|
|
11092
|
+
onChange: (event) => {
|
|
11093
|
+
handleFileChange(event);
|
|
11094
|
+
}
|
|
11095
|
+
}),
|
|
11096
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11097
|
+
className: KnowledgeView_module_css_default.statsText,
|
|
11098
|
+
children: [
|
|
11099
|
+
filteredCards.length,
|
|
11100
|
+
" ",
|
|
11101
|
+
t("kv.stats.cards"),
|
|
11102
|
+
" · ",
|
|
11103
|
+
clusterCount,
|
|
11104
|
+
" ",
|
|
11105
|
+
t("kv.stats.clusters")
|
|
11106
|
+
]
|
|
11107
|
+
})
|
|
11108
|
+
]
|
|
11109
|
+
})]
|
|
11110
|
+
}),
|
|
11111
|
+
exportTooLarge && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11112
|
+
className: `${KnowledgeView_module_css_default.ioNotice} ${KnowledgeView_module_css_default.ioNoticeError} ${KnowledgeView_module_css_default.ioBanner}`,
|
|
11113
|
+
role: "status",
|
|
11114
|
+
children: t("kv.io.exportTooLarge")
|
|
11115
|
+
}),
|
|
11116
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11117
|
+
className: KnowledgeView_module_css_default.mainArea,
|
|
11118
|
+
children: [
|
|
11119
|
+
cards.length === 0 && !loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11120
|
+
className: KnowledgeView_module_css_default.emptyState,
|
|
11121
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11122
|
+
className: KnowledgeView_module_css_default.emptyIcon,
|
|
11123
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconKnowledge, { size: 40 })
|
|
11124
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11125
|
+
className: KnowledgeView_module_css_default.emptyHint,
|
|
11126
|
+
children: t("kv.empty.hint")
|
|
10532
11127
|
})]
|
|
11128
|
+
}) : filteredCards.length === 0 && !loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11129
|
+
className: KnowledgeView_module_css_default.emptyState,
|
|
11130
|
+
children: [
|
|
11131
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11132
|
+
className: KnowledgeView_module_css_default.emptyIcon,
|
|
11133
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconKnowledge, { size: 32 })
|
|
11134
|
+
}),
|
|
11135
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11136
|
+
className: KnowledgeView_module_css_default.emptyHint,
|
|
11137
|
+
children: t("kv.empty.filtered")
|
|
11138
|
+
}),
|
|
11139
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11140
|
+
type: "button",
|
|
11141
|
+
className: KnowledgeView_module_css_default.resetBtn,
|
|
11142
|
+
onClick: handleResetFilters,
|
|
11143
|
+
children: t("kv.filter.reset")
|
|
11144
|
+
})
|
|
11145
|
+
]
|
|
11146
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11147
|
+
className: KnowledgeView_module_css_default.graphCanvas,
|
|
11148
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KnowledgeGraph, {
|
|
11149
|
+
ref: graphRef,
|
|
11150
|
+
data: graphData,
|
|
11151
|
+
selectedCardId: selectedCard?.id,
|
|
11152
|
+
onSelectCard: handleSelectCard,
|
|
11153
|
+
onTagClick: handleTagClick
|
|
11154
|
+
})
|
|
10533
11155
|
}),
|
|
10534
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10535
|
-
className: KnowledgeView_module_css_default.
|
|
11156
|
+
selectedCard && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11157
|
+
className: KnowledgeView_module_css_default.drawer,
|
|
11158
|
+
role: "dialog",
|
|
11159
|
+
"aria-modal": "false",
|
|
10536
11160
|
children: [
|
|
10537
11161
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10538
|
-
className: KnowledgeView_module_css_default.
|
|
10539
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
10540
|
-
className: KnowledgeView_module_css_default.
|
|
10541
|
-
children:
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
|
|
11162
|
+
className: KnowledgeView_module_css_default.drawerHeader,
|
|
11163
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11164
|
+
className: KnowledgeView_module_css_default.drawerTitleGroup,
|
|
11165
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11166
|
+
className: KnowledgeView_module_css_default.drawerTitle,
|
|
11167
|
+
children: selectedCard.title
|
|
11168
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11169
|
+
className: KnowledgeView_module_css_default.metaRow,
|
|
11170
|
+
children: [
|
|
11171
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11172
|
+
className: `${KnowledgeView_module_css_default.badge} ${KnowledgeView_module_css_default.badgePlatform}`,
|
|
11173
|
+
children: selectedCard.source.type
|
|
11174
|
+
}),
|
|
11175
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11176
|
+
className: `${KnowledgeView_module_css_default.badge} ${selectedCard.credibility === "high" ? KnowledgeView_module_css_default.badgeHigh : selectedCard.credibility === "medium" ? KnowledgeView_module_css_default.badgeMedium : KnowledgeView_module_css_default.badgeLow}`,
|
|
11177
|
+
children: selectedCard.credibility
|
|
11178
|
+
}),
|
|
11179
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: selectedCard.source.author }),
|
|
11180
|
+
selectedCard.source.publishedAt && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: selectedCard.source.publishedAt })
|
|
11181
|
+
]
|
|
11182
|
+
})]
|
|
11183
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11184
|
+
type: "button",
|
|
11185
|
+
className: KnowledgeView_module_css_default.closeBtn,
|
|
11186
|
+
onClick: () => setSelectedCard(null),
|
|
11187
|
+
"aria-label": "Close",
|
|
11188
|
+
children: "✕"
|
|
10545
11189
|
})]
|
|
10546
11190
|
}),
|
|
10547
11191
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10548
|
-
className: KnowledgeView_module_css_default.
|
|
11192
|
+
className: KnowledgeView_module_css_default.drawerBody,
|
|
10549
11193
|
children: [
|
|
10550
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
10551
|
-
className: KnowledgeView_module_css_default.
|
|
10552
|
-
children:
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
className: `${KnowledgeView_module_css_default.factBucket} ${KnowledgeView_module_css_default.factVerified}`,
|
|
10556
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10557
|
-
className: KnowledgeView_module_css_default.bucketTitle,
|
|
10558
|
-
children: ["✅ ", t("kv.drawer.verified")]
|
|
11194
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11195
|
+
className: KnowledgeView_module_css_default.section,
|
|
11196
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11197
|
+
className: KnowledgeView_module_css_default.sectionLabel,
|
|
11198
|
+
children: t("kv.drawer.coreClaims")
|
|
10559
11199
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
10560
|
-
className: KnowledgeView_module_css_default.
|
|
10561
|
-
children: selectedCard.
|
|
11200
|
+
className: KnowledgeView_module_css_default.claimsList,
|
|
11201
|
+
children: selectedCard.coreClaims.map((claim, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: claim }, idx))
|
|
10562
11202
|
})]
|
|
10563
11203
|
}),
|
|
10564
|
-
|
|
10565
|
-
className:
|
|
10566
|
-
children: [
|
|
10567
|
-
|
|
10568
|
-
|
|
11204
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11205
|
+
className: KnowledgeView_module_css_default.section,
|
|
11206
|
+
children: [
|
|
11207
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11208
|
+
className: KnowledgeView_module_css_default.sectionLabel,
|
|
11209
|
+
children: t("kv.drawer.factCheck")
|
|
11210
|
+
}),
|
|
11211
|
+
selectedCard.factCheck.verified.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11212
|
+
className: `${KnowledgeView_module_css_default.factBucket} ${KnowledgeView_module_css_default.factVerified}`,
|
|
11213
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11214
|
+
className: KnowledgeView_module_css_default.bucketTitle,
|
|
11215
|
+
children: ["✅ ", t("kv.drawer.verified")]
|
|
11216
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
11217
|
+
className: KnowledgeView_module_css_default.bucketList,
|
|
11218
|
+
children: selectedCard.factCheck.verified.map((v, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: v }, i))
|
|
11219
|
+
})]
|
|
11220
|
+
}),
|
|
11221
|
+
selectedCard.factCheck.discrepancies.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11222
|
+
className: `${KnowledgeView_module_css_default.factBucket} ${KnowledgeView_module_css_default.factDiscrepancies}`,
|
|
11223
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11224
|
+
className: KnowledgeView_module_css_default.bucketTitle,
|
|
11225
|
+
children: ["⚠️ ", t("kv.drawer.discrepancies")]
|
|
11226
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
11227
|
+
className: KnowledgeView_module_css_default.bucketList,
|
|
11228
|
+
children: selectedCard.factCheck.discrepancies.map((d, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: d }, i))
|
|
11229
|
+
})]
|
|
11230
|
+
}),
|
|
11231
|
+
selectedCard.factCheck.unverifiable.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11232
|
+
className: `${KnowledgeView_module_css_default.factBucket} ${KnowledgeView_module_css_default.factUnverifiable}`,
|
|
11233
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11234
|
+
className: KnowledgeView_module_css_default.bucketTitle,
|
|
11235
|
+
children: ["❓ ", t("kv.drawer.unverifiable")]
|
|
11236
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
11237
|
+
className: KnowledgeView_module_css_default.bucketList,
|
|
11238
|
+
children: selectedCard.factCheck.unverifiable.map((u, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: u }, i))
|
|
11239
|
+
})]
|
|
11240
|
+
})
|
|
11241
|
+
]
|
|
11242
|
+
}),
|
|
11243
|
+
selectedCard.takeaways.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11244
|
+
className: KnowledgeView_module_css_default.section,
|
|
11245
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11246
|
+
className: KnowledgeView_module_css_default.sectionLabel,
|
|
11247
|
+
children: t("kv.drawer.takeaways")
|
|
10569
11248
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
10570
|
-
className: KnowledgeView_module_css_default.
|
|
10571
|
-
children: selectedCard.
|
|
11249
|
+
className: KnowledgeView_module_css_default.claimsList,
|
|
11250
|
+
children: selectedCard.takeaways.map((item, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: item }, idx))
|
|
10572
11251
|
})]
|
|
10573
11252
|
}),
|
|
10574
|
-
selectedCard.
|
|
10575
|
-
className:
|
|
10576
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
10577
|
-
className: KnowledgeView_module_css_default.
|
|
10578
|
-
children:
|
|
11253
|
+
selectedCard.boundaries.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11254
|
+
className: KnowledgeView_module_css_default.section,
|
|
11255
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11256
|
+
className: KnowledgeView_module_css_default.sectionLabel,
|
|
11257
|
+
children: t("kv.drawer.boundaries")
|
|
10579
11258
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
10580
|
-
className: KnowledgeView_module_css_default.
|
|
10581
|
-
children: selectedCard.
|
|
11259
|
+
className: KnowledgeView_module_css_default.claimsList,
|
|
11260
|
+
children: selectedCard.boundaries.map((item, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: item }, idx))
|
|
11261
|
+
})]
|
|
11262
|
+
}),
|
|
11263
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11264
|
+
className: KnowledgeView_module_css_default.section,
|
|
11265
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11266
|
+
className: KnowledgeView_module_css_default.sectionLabel,
|
|
11267
|
+
children: t("kv.filter.tag")
|
|
11268
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11269
|
+
className: KnowledgeView_module_css_default.tagPills,
|
|
11270
|
+
children: selectedCard.tags.map((tag) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
11271
|
+
className: KnowledgeView_module_css_default.tagPill,
|
|
11272
|
+
children: ["#", tag]
|
|
11273
|
+
}, tag))
|
|
11274
|
+
})]
|
|
11275
|
+
}),
|
|
11276
|
+
selectedCard.tickers && selectedCard.tickers.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11277
|
+
className: KnowledgeView_module_css_default.section,
|
|
11278
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11279
|
+
className: KnowledgeView_module_css_default.sectionLabel,
|
|
11280
|
+
children: t("kv.drawer.tickers")
|
|
11281
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11282
|
+
className: KnowledgeView_module_css_default.tagPills,
|
|
11283
|
+
children: selectedCard.tickers.map((sym) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
11284
|
+
className: KnowledgeView_module_css_default.tagPill,
|
|
11285
|
+
children: ["$", sym]
|
|
11286
|
+
}, sym))
|
|
11287
|
+
})]
|
|
11288
|
+
}),
|
|
11289
|
+
selectedCard.related && selectedCard.related.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11290
|
+
className: KnowledgeView_module_css_default.section,
|
|
11291
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11292
|
+
className: KnowledgeView_module_css_default.sectionLabel,
|
|
11293
|
+
children: t("kv.drawer.related")
|
|
11294
|
+
}), selectedCard.related.map((relId) => {
|
|
11295
|
+
const relCard = cards.find((c) => c.id === relId);
|
|
11296
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11297
|
+
className: KnowledgeView_module_css_default.relatedItem,
|
|
11298
|
+
onClick: () => handleJumpToRelated(relId),
|
|
11299
|
+
children: ["🔗 ", relCard?.title ?? relId]
|
|
11300
|
+
}, relId);
|
|
10582
11301
|
})]
|
|
10583
11302
|
})
|
|
10584
11303
|
]
|
|
10585
11304
|
}),
|
|
10586
|
-
selectedCard.
|
|
10587
|
-
className: KnowledgeView_module_css_default.
|
|
10588
|
-
children:
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
className: KnowledgeView_module_css_default.
|
|
10593
|
-
children:
|
|
10594
|
-
})
|
|
10595
|
-
}),
|
|
10596
|
-
selectedCard.boundaries.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10597
|
-
className: KnowledgeView_module_css_default.section,
|
|
10598
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10599
|
-
className: KnowledgeView_module_css_default.sectionLabel,
|
|
10600
|
-
children: t("kv.drawer.boundaries")
|
|
10601
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
10602
|
-
className: KnowledgeView_module_css_default.claimsList,
|
|
10603
|
-
children: selectedCard.boundaries.map((item, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: item }, idx))
|
|
10604
|
-
})]
|
|
10605
|
-
}),
|
|
10606
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10607
|
-
className: KnowledgeView_module_css_default.section,
|
|
10608
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10609
|
-
className: KnowledgeView_module_css_default.sectionLabel,
|
|
10610
|
-
children: t("kv.filter.tag")
|
|
10611
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10612
|
-
className: KnowledgeView_module_css_default.tagPills,
|
|
10613
|
-
children: selectedCard.tags.map((tag) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
10614
|
-
className: KnowledgeView_module_css_default.tagPill,
|
|
10615
|
-
children: ["#", tag]
|
|
10616
|
-
}, tag))
|
|
10617
|
-
})]
|
|
10618
|
-
}),
|
|
10619
|
-
selectedCard.tickers && selectedCard.tickers.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10620
|
-
className: KnowledgeView_module_css_default.section,
|
|
10621
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10622
|
-
className: KnowledgeView_module_css_default.sectionLabel,
|
|
10623
|
-
children: t("kv.drawer.tickers")
|
|
10624
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10625
|
-
className: KnowledgeView_module_css_default.tagPills,
|
|
10626
|
-
children: selectedCard.tickers.map((sym) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
10627
|
-
className: KnowledgeView_module_css_default.tagPill,
|
|
10628
|
-
children: ["$", sym]
|
|
10629
|
-
}, sym))
|
|
10630
|
-
})]
|
|
10631
|
-
}),
|
|
10632
|
-
selectedCard.related && selectedCard.related.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10633
|
-
className: KnowledgeView_module_css_default.section,
|
|
10634
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10635
|
-
className: KnowledgeView_module_css_default.sectionLabel,
|
|
10636
|
-
children: t("kv.drawer.related")
|
|
10637
|
-
}), selectedCard.related.map((relId) => {
|
|
10638
|
-
const relCard = cards.find((c) => c.id === relId);
|
|
10639
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10640
|
-
className: KnowledgeView_module_css_default.relatedItem,
|
|
10641
|
-
onClick: () => handleJumpToRelated(relId),
|
|
10642
|
-
children: ["🔗 ", relCard?.title ?? relId]
|
|
10643
|
-
}, relId);
|
|
10644
|
-
})]
|
|
11305
|
+
selectedCard.source.url && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11306
|
+
className: KnowledgeView_module_css_default.drawerFooter,
|
|
11307
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
11308
|
+
href: selectedCard.source.url,
|
|
11309
|
+
target: "_blank",
|
|
11310
|
+
rel: "noopener noreferrer",
|
|
11311
|
+
className: KnowledgeView_module_css_default.openSourceBtn,
|
|
11312
|
+
children: [t("kv.drawer.openSource"), " ↗"]
|
|
11313
|
+
})
|
|
10645
11314
|
})
|
|
10646
11315
|
]
|
|
10647
11316
|
}),
|
|
10648
|
-
|
|
10649
|
-
className: KnowledgeView_module_css_default.
|
|
10650
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
|
|
10654
|
-
|
|
10655
|
-
children: [
|
|
11317
|
+
draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11318
|
+
className: KnowledgeView_module_css_default.ioOverlay,
|
|
11319
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11320
|
+
className: KnowledgeView_module_css_default.ioPanel,
|
|
11321
|
+
role: "dialog",
|
|
11322
|
+
"aria-modal": "false",
|
|
11323
|
+
"aria-label": t("kv.io.dialogTitle"),
|
|
11324
|
+
children: [
|
|
11325
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11326
|
+
className: KnowledgeView_module_css_default.ioPanelHeader,
|
|
11327
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11328
|
+
className: KnowledgeView_module_css_default.ioPanelTitle,
|
|
11329
|
+
children: t("kv.io.dialogTitle")
|
|
11330
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11331
|
+
type: "button",
|
|
11332
|
+
className: KnowledgeView_module_css_default.closeBtn,
|
|
11333
|
+
onClick: closeImportPanel,
|
|
11334
|
+
"aria-label": t("kv.io.cancel"),
|
|
11335
|
+
children: "✕"
|
|
11336
|
+
})]
|
|
11337
|
+
}),
|
|
11338
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11339
|
+
className: KnowledgeView_module_css_default.ioPanelBody,
|
|
11340
|
+
children: [
|
|
11341
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11342
|
+
className: KnowledgeView_module_css_default.ioFileHint,
|
|
11343
|
+
children: [
|
|
11344
|
+
draft.fileName,
|
|
11345
|
+
" — ",
|
|
11346
|
+
t("kv.io.fileHint")
|
|
11347
|
+
]
|
|
11348
|
+
}),
|
|
11349
|
+
importError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11350
|
+
className: `${KnowledgeView_module_css_default.ioNotice} ${KnowledgeView_module_css_default.ioNoticeError}`,
|
|
11351
|
+
children: [t("kv.io.failed"), importError === "" ? "" : `: ${importError}`]
|
|
11352
|
+
}),
|
|
11353
|
+
applied !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11354
|
+
className: `${KnowledgeView_module_css_default.ioNotice} ${KnowledgeView_module_css_default.ioNoticeOk}`,
|
|
11355
|
+
children: [
|
|
11356
|
+
t("kv.io.applied"),
|
|
11357
|
+
": ",
|
|
11358
|
+
t("kv.io.planCreate"),
|
|
11359
|
+
" ",
|
|
11360
|
+
applied.created,
|
|
11361
|
+
" ·",
|
|
11362
|
+
" ",
|
|
11363
|
+
t("kv.io.planUpdate"),
|
|
11364
|
+
" ",
|
|
11365
|
+
applied.updated,
|
|
11366
|
+
" · ",
|
|
11367
|
+
t("kv.io.planSkip"),
|
|
11368
|
+
" ",
|
|
11369
|
+
applied.skipped
|
|
11370
|
+
]
|
|
11371
|
+
}),
|
|
11372
|
+
applied === null && draft.bundle !== null && importCounts !== null && importPlan !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
11373
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11374
|
+
className: KnowledgeView_module_css_default.ioModes,
|
|
11375
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
11376
|
+
className: KnowledgeView_module_css_default.ioMode,
|
|
11377
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
11378
|
+
type: "radio",
|
|
11379
|
+
name: "dshtrading-knowledge-import-mode",
|
|
11380
|
+
value: "skip",
|
|
11381
|
+
checked: draft.mode === "skip",
|
|
11382
|
+
onChange: () => handleSelectImportMode("skip"),
|
|
11383
|
+
disabled: importing
|
|
11384
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("kv.io.modeSkip") })]
|
|
11385
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
11386
|
+
className: KnowledgeView_module_css_default.ioMode,
|
|
11387
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
11388
|
+
type: "radio",
|
|
11389
|
+
name: "dshtrading-knowledge-import-mode",
|
|
11390
|
+
value: "overwrite",
|
|
11391
|
+
checked: draft.mode === "overwrite",
|
|
11392
|
+
onChange: () => handleSelectImportMode("overwrite"),
|
|
11393
|
+
disabled: importing
|
|
11394
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("kv.io.modeOverwrite") })]
|
|
11395
|
+
})]
|
|
11396
|
+
}),
|
|
11397
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11398
|
+
className: KnowledgeView_module_css_default.ioCounts,
|
|
11399
|
+
children: [
|
|
11400
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11401
|
+
className: KnowledgeView_module_css_default.ioCount,
|
|
11402
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11403
|
+
className: KnowledgeView_module_css_default.ioCountValue,
|
|
11404
|
+
children: importCounts.create
|
|
11405
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11406
|
+
className: KnowledgeView_module_css_default.ioCountLabel,
|
|
11407
|
+
children: t("kv.io.planCreate")
|
|
11408
|
+
})]
|
|
11409
|
+
}),
|
|
11410
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11411
|
+
className: KnowledgeView_module_css_default.ioCount,
|
|
11412
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11413
|
+
className: KnowledgeView_module_css_default.ioCountValue,
|
|
11414
|
+
children: importCounts.update
|
|
11415
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11416
|
+
className: KnowledgeView_module_css_default.ioCountLabel,
|
|
11417
|
+
children: t("kv.io.planUpdate")
|
|
11418
|
+
})]
|
|
11419
|
+
}),
|
|
11420
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11421
|
+
className: KnowledgeView_module_css_default.ioCount,
|
|
11422
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11423
|
+
className: KnowledgeView_module_css_default.ioCountValue,
|
|
11424
|
+
children: importCounts.skip
|
|
11425
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11426
|
+
className: KnowledgeView_module_css_default.ioCountLabel,
|
|
11427
|
+
children: t("kv.io.planSkip")
|
|
11428
|
+
})]
|
|
11429
|
+
}),
|
|
11430
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11431
|
+
className: KnowledgeView_module_css_default.ioCount,
|
|
11432
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11433
|
+
className: KnowledgeView_module_css_default.ioCountValue,
|
|
11434
|
+
children: importCounts.dropped
|
|
11435
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11436
|
+
className: KnowledgeView_module_css_default.ioCountLabel,
|
|
11437
|
+
children: t("kv.io.planDropped")
|
|
11438
|
+
})]
|
|
11439
|
+
}),
|
|
11440
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11441
|
+
className: KnowledgeView_module_css_default.ioCount,
|
|
11442
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11443
|
+
className: KnowledgeView_module_css_default.ioCountValue,
|
|
11444
|
+
children: importCounts.invalid
|
|
11445
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11446
|
+
className: KnowledgeView_module_css_default.ioCountLabel,
|
|
11447
|
+
children: t("kv.io.planInvalid")
|
|
11448
|
+
})]
|
|
11449
|
+
})
|
|
11450
|
+
]
|
|
11451
|
+
}),
|
|
11452
|
+
importCounts.invalid > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11453
|
+
className: KnowledgeView_module_css_default.ioInvalid,
|
|
11454
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11455
|
+
className: KnowledgeView_module_css_default.ioInvalidTitle,
|
|
11456
|
+
children: [
|
|
11457
|
+
t("kv.io.planInvalid"),
|
|
11458
|
+
" ",
|
|
11459
|
+
importCounts.invalid
|
|
11460
|
+
]
|
|
11461
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
11462
|
+
className: KnowledgeView_module_css_default.ioInvalidList,
|
|
11463
|
+
children: importPlan.invalid.slice(0, 5).map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
11464
|
+
"#",
|
|
11465
|
+
item.index,
|
|
11466
|
+
" ",
|
|
11467
|
+
item.error
|
|
11468
|
+
] }, item.index))
|
|
11469
|
+
})]
|
|
11470
|
+
})
|
|
11471
|
+
] })
|
|
11472
|
+
]
|
|
11473
|
+
}),
|
|
11474
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11475
|
+
className: KnowledgeView_module_css_default.ioPanelFooter,
|
|
11476
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11477
|
+
type: "button",
|
|
11478
|
+
className: KnowledgeView_module_css_default.ioBtn,
|
|
11479
|
+
onClick: closeImportPanel,
|
|
11480
|
+
disabled: importing,
|
|
11481
|
+
children: t("kv.io.cancel")
|
|
11482
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11483
|
+
type: "button",
|
|
11484
|
+
className: `${KnowledgeView_module_css_default.ioBtn} ${KnowledgeView_module_css_default.ioBtnPrimary}`,
|
|
11485
|
+
onClick: () => {
|
|
11486
|
+
handleConfirmImport();
|
|
11487
|
+
},
|
|
11488
|
+
disabled: importing || applied !== null || draft.bundle === null,
|
|
11489
|
+
children: t("kv.io.confirm")
|
|
11490
|
+
})]
|
|
11491
|
+
})
|
|
11492
|
+
]
|
|
10656
11493
|
})
|
|
10657
11494
|
})
|
|
10658
11495
|
]
|
|
10659
|
-
})
|
|
10660
|
-
|
|
11496
|
+
})
|
|
11497
|
+
]
|
|
10661
11498
|
});
|
|
10662
11499
|
}
|
|
10663
11500
|
//#endregion
|
|
@@ -10689,7 +11526,24 @@ window.__ModuleLoader__.load({
|
|
|
10689
11526
|
"kv.drawer.boundaries": "适用边界与避坑",
|
|
10690
11527
|
"kv.drawer.tickers": "关联标的",
|
|
10691
11528
|
"kv.drawer.related": "关联卡片",
|
|
10692
|
-
"kv.drawer.openSource": "打开原始链接"
|
|
11529
|
+
"kv.drawer.openSource": "打开原始链接",
|
|
11530
|
+
"kv.io.export": "导出",
|
|
11531
|
+
"kv.io.import": "导入",
|
|
11532
|
+
"kv.io.exportEmpty": "当前筛选结果为空,没有可导出的卡片",
|
|
11533
|
+
"kv.io.dialogTitle": "导入知识库文件",
|
|
11534
|
+
"kv.io.modeSkip": "跳过:库里已有同来源链接的卡片保持原样",
|
|
11535
|
+
"kv.io.modeOverwrite": "覆盖:用文件内容改写同来源链接的卡片,保留本机卡片 id",
|
|
11536
|
+
"kv.io.planCreate": "新增",
|
|
11537
|
+
"kv.io.planUpdate": "更新",
|
|
11538
|
+
"kv.io.planSkip": "跳过",
|
|
11539
|
+
"kv.io.planDropped": "丢弃关联",
|
|
11540
|
+
"kv.io.planInvalid": "非法",
|
|
11541
|
+
"kv.io.confirm": "确认导入",
|
|
11542
|
+
"kv.io.cancel": "取消",
|
|
11543
|
+
"kv.io.applied": "导入完成",
|
|
11544
|
+
"kv.io.failed": "导入失败",
|
|
11545
|
+
"kv.io.fileHint": "预览按本机现有卡片计算,确认后才会写入知识库",
|
|
11546
|
+
"kv.io.exportTooLarge": "导出内容超过上限(2000 张卡片或 8 MiB):请先缩小筛选范围再导出,否则文件导不回来"
|
|
10693
11547
|
};
|
|
10694
11548
|
const en = {
|
|
10695
11549
|
"kv.search.placeholder": "Search title, summary, tags...",
|
|
@@ -10718,7 +11572,24 @@ window.__ModuleLoader__.load({
|
|
|
10718
11572
|
"kv.drawer.boundaries": "Boundaries & Pitfalls",
|
|
10719
11573
|
"kv.drawer.tickers": "Related Tickers",
|
|
10720
11574
|
"kv.drawer.related": "Related Cards",
|
|
10721
|
-
"kv.drawer.openSource": "Open Original URL"
|
|
11575
|
+
"kv.drawer.openSource": "Open Original URL",
|
|
11576
|
+
"kv.io.export": "Export",
|
|
11577
|
+
"kv.io.import": "Import",
|
|
11578
|
+
"kv.io.exportEmpty": "No cards in the current filter — nothing to export",
|
|
11579
|
+
"kv.io.dialogTitle": "Import knowledge bundle",
|
|
11580
|
+
"kv.io.modeSkip": "Skip: cards with the same source link already on this machine stay unchanged",
|
|
11581
|
+
"kv.io.modeOverwrite": "Overwrite: replace cards with the same source link, keeping their local card ids",
|
|
11582
|
+
"kv.io.planCreate": "Create",
|
|
11583
|
+
"kv.io.planUpdate": "Update",
|
|
11584
|
+
"kv.io.planSkip": "Skip",
|
|
11585
|
+
"kv.io.planDropped": "Dropped links",
|
|
11586
|
+
"kv.io.planInvalid": "Invalid",
|
|
11587
|
+
"kv.io.confirm": "Import now",
|
|
11588
|
+
"kv.io.cancel": "Cancel",
|
|
11589
|
+
"kv.io.applied": "Import complete",
|
|
11590
|
+
"kv.io.failed": "Import failed",
|
|
11591
|
+
"kv.io.fileHint": "Preview is computed against the cards already on this machine; nothing is written until you confirm",
|
|
11592
|
+
"kv.io.exportTooLarge": "Export exceeds the limit (2000 cards or 8 MiB): narrow the filter before exporting, otherwise the file cannot be imported back"
|
|
10722
11593
|
};
|
|
10723
11594
|
//#endregion
|
|
10724
11595
|
//#region src/client/index.ts
|
|
@@ -10738,7 +11609,8 @@ window.__ModuleLoader__.load({
|
|
|
10738
11609
|
const faces = scope;
|
|
10739
11610
|
const bridge = {
|
|
10740
11611
|
fetchKnowledgeCards: () => faces.tradingBridge.fetchKnowledgeCards(),
|
|
10741
|
-
subscribeTradingEvents: (handlers) => faces.tradingBridge.subscribeTradingEvents(handlers)
|
|
11612
|
+
subscribeTradingEvents: (handlers) => faces.tradingBridge.subscribeTradingEvents(handlers),
|
|
11613
|
+
importKnowledgeBundle: (input) => faces.tradingBridge.importKnowledgeBundle(input)
|
|
10742
11614
|
};
|
|
10743
11615
|
faces.tradingStageViews.register({
|
|
10744
11616
|
id: "knowledge",
|