dsh-plugin-term-dictionary 0.0.0-stage → 1.0.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/CHANGELOG.md +128 -0
- package/LICENSE +21 -0
- package/README.md +772 -2
- package/cordis.patch.yml +14 -0
- package/icon.svg +13 -0
- package/lib/ROADMAP-lexicon.md +52 -0
- package/lib/client.js +12586 -0
- package/lib/core/api.js +278 -0
- package/lib/core/bus.js +98 -0
- package/lib/core/copy.js +552 -0
- package/lib/core/core.js +309 -0
- package/lib/core/dictionary.js +1083 -0
- package/lib/core/entries.js +419 -0
- package/lib/core/highlight.js +282 -0
- package/lib/core/hover.js +470 -0
- package/lib/core/hovercard.js +173 -0
- package/lib/core/interact.js +1802 -0
- package/lib/core/lexicon.en.js +872 -0
- package/lib/core/lexicon.zh.js +249 -0
- package/lib/core/overlay.js +239 -0
- package/lib/core/pack.js +345 -0
- package/lib/core/package.json +4 -0
- package/lib/core/selection.js +83 -0
- package/lib/core/settings.js +312 -0
- package/lib/core/shell.js +1003 -0
- package/lib/core/stopwords.js +147 -0
- package/lib/core/store.js +370 -0
- package/lib/core/styles.js +557 -0
- package/lib/core/terms.js +398 -0
- package/lib/core/transfer.js +312 -0
- package/lib/core/views.js +2056 -0
- package/lib/index.js +1110 -0
- package/lib/pack-code.js +84 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +71 -3
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Built-in technical lexicon: Chinese industry jargon the plugin recognises
|
|
4
|
+
* without any network call. Keys are Chinese terms; `zh` gives a short
|
|
5
|
+
* paraphrase and `gloss` explains the concept in one sentence.
|
|
6
|
+
*/
|
|
7
|
+
module.exports = {
|
|
8
|
+
"幂等": { zh: "幂等操作", domain: "软件工程", gloss: "同一请求执行一次与重复执行多次,对系统状态造成的影响完全相同。" },
|
|
9
|
+
"旁路": { zh: "旁路处理", domain: "系统", gloss: "让流量或数据绕过主链路处理,常用于降级、灰度与旁路校验。" },
|
|
10
|
+
"灰度发布": { zh: "灰度上线", domain: "运维", gloss: "只让一部分用户先用上新版本,观察稳定后再逐步放量到全量。" },
|
|
11
|
+
"雪崩": { zh: "服务雪崩", domain: "运维", gloss: "某个下游变慢导致调用方线程被占满,故障沿依赖链层层放大。" },
|
|
12
|
+
"脑裂": { zh: "集群脑裂", domain: "系统", gloss: "网络分区后两侧都自认是主节点,同时写数据造成冲突与不一致。" },
|
|
13
|
+
"限流": { zh: "流量限制", domain: "运维", gloss: "限制单位时间内的请求数量,把流量控制在系统能承受的范围内。" },
|
|
14
|
+
"熔断": { zh: "熔断保护", domain: "运维", gloss: "下游错误率超过阈值就快速失败并暂停调用,给它恢复的时间。" },
|
|
15
|
+
"埋点": { zh: "数据埋点", domain: "数据", gloss: "在代码中插入采集点,记录用户行为与关键路径上的事件数据。" },
|
|
16
|
+
"对账": { zh: "账务核对", domain: "数据", gloss: "把两套独立记录逐笔比对,找出并修正金额或状态的差异。" },
|
|
17
|
+
"清结算": { zh: "清算与结算", domain: "数据", gloss: "先算清各方应收应付,再完成资金实际划拨的两个连续环节。" },
|
|
18
|
+
"风控": { zh: "风险控制", domain: "安全", gloss: "用规则与模型识别欺诈、套现等异常行为并及时拦截。" },
|
|
19
|
+
"中台": { zh: "业务中台", domain: "产品", gloss: "把多个业务线共用的能力抽出来统一建设,供前台快速复用。" },
|
|
20
|
+
"闭环": { zh: "流程闭环", domain: "产品", gloss: "从输入到反馈形成完整回路,结果能反过来驱动下一轮改进。" },
|
|
21
|
+
"抓手": { zh: "着力点", domain: "产品", gloss: "能撬动整体目标、可落地执行的那个关键切入点或具体动作。" },
|
|
22
|
+
"熵增": { zh: "无序度增加", domain: "系统", gloss: "系统若不持续投入治理,混乱与维护成本会自然上升的趋势。" },
|
|
23
|
+
"势能": { zh: "积累优势", domain: "产品", gloss: "前期积累的资源、口碑或规模,可在后续转化为更强的增长动力。" },
|
|
24
|
+
"护城河": { zh: "竞争壁垒", domain: "产品", gloss: "竞争对手短期内难以复制、能持续保护自身优势的结构性能力。" },
|
|
25
|
+
"渗透率": { zh: "市场渗透率", domain: "产品", gloss: "目标人群中已使用该产品或功能的比例,反映普及程度。" },
|
|
26
|
+
"客单价": { zh: "平均客单价", domain: "产品", gloss: "平均每位付费用户贡献的交易金额,等于总收入除以付费人数。" },
|
|
27
|
+
"漏斗": { zh: "转化漏斗", domain: "产品", gloss: "用户从进入到最后转化逐层流失的过程,每层人数依次减少。" },
|
|
28
|
+
"归因": { zh: "效果归因", domain: "数据", gloss: "把最终转化按规则分摊到各个触点上,判断谁真正带来了效果。" },
|
|
29
|
+
"冷启动": { zh: "冷启动阶段", domain: "产品", gloss: "缺少历史数据或用户时系统难以给出有效推荐的起步阶段。" },
|
|
30
|
+
"破圈": { zh: "突破圈层", domain: "产品", gloss: "产品走出原有小众人群,被更广泛的大众用户接受和使用。" },
|
|
31
|
+
"盘活": { zh: "激活存量", domain: "产品", gloss: "让沉睡的用户、数据或资源重新流动起来并产生价值。" },
|
|
32
|
+
"拉通": { zh: "跨团队拉通", domain: "产品", gloss: "把多个团队的信息与目标对齐,消除各说各话的协作断层。" },
|
|
33
|
+
"对齐": { zh: "达成共识", domain: "产品", gloss: "相关方把目标、口径与节奏统一到同一认识上,避免各做各的。" },
|
|
34
|
+
"沉淀": { zh: "积累沉淀", domain: "软件工程", gloss: "把一次性的经验或代码整理成可长期复用的资产保留下来。" },
|
|
35
|
+
"复用": { zh: "重复使用", domain: "软件工程", gloss: "让已有能力被多处直接使用,避免重复建设与重复维护。" },
|
|
36
|
+
"水位": { zh: "负载水位", domain: "运维", gloss: "当前负载占系统容量的比例,用来判断是否需要扩容或限流。" },
|
|
37
|
+
"基线": { zh: "基准参照", domain: "数据", gloss: "作为对比参照的稳定版本或指标值,用来衡量变化是否异常。" },
|
|
38
|
+
"回滚": { zh: "版本回退", domain: "运维", gloss: "新版本出问题时撤回改动,使系统恢复到上一个可用状态。" },
|
|
39
|
+
"兜底": { zh: "兜底方案", domain: "软件工程", gloss: "主流程失败时保证结果仍可接受的最低限度保障逻辑。" },
|
|
40
|
+
"脱敏": { zh: "数据脱敏", domain: "安全", gloss: "对敏感字段做模糊或替换处理,使数据可用但不暴露真实信息。" },
|
|
41
|
+
"穿透": { zh: "穿透查询", domain: "数据", gloss: "跳过中间缓存或汇总层,直接访问底层数据源获取明细。" },
|
|
42
|
+
"染色": { zh: "流量染色", domain: "运维", gloss: "给请求打上标记,使它在全链路中被识别并只走指定的环境。" },
|
|
43
|
+
"灰度": { zh: "灰度放量", domain: "运维", gloss: "新功能先对少量用户开放,按比例逐步扩大直到全量。" },
|
|
44
|
+
"压测": { zh: "压力测试", domain: "运维", gloss: "用模拟的高并发流量检验系统容量、瓶颈与稳定性。" },
|
|
45
|
+
"全链路": { zh: "端到端链路", domain: "运维", gloss: "从用户入口一直到最底层依赖的完整调用路径。" },
|
|
46
|
+
"可观测性": { zh: "系统可观测", domain: "运维", gloss: "仅凭指标、日志与链路数据就能推断系统内部状态的能力。" },
|
|
47
|
+
"混沌工程": { zh: "故障注入实践", domain: "运维", gloss: "主动制造故障来验证系统容错能力,把未知风险提前暴露出来。" },
|
|
48
|
+
"单元化": { zh: "单元化架构", domain: "系统", gloss: "把系统切成若干自包含单元,每个单元能独立承接一部分用户流量。" },
|
|
49
|
+
"多活": { zh: "多机房多活", domain: "系统", gloss: "多个机房同时对外提供服务,任一机房故障可快速切换承接流量。" },
|
|
50
|
+
"同城双活": { zh: "同城双机房", domain: "系统", gloss: "同城两个机房同时承载流量,延迟低但无法抵御城市级灾难。" },
|
|
51
|
+
"异地多活": { zh: "跨地域多活", domain: "系统", gloss: "多个城市机房同时读写并保持数据同步,可抵御区域性灾难。" },
|
|
52
|
+
"分库分表": { zh: "数据库拆分", domain: "数据", gloss: "把单库单表按规则拆成多库多表,以突破单机容量与性能上限。" },
|
|
53
|
+
"读写分离": { zh: "主写从读", domain: "数据", gloss: "写请求走主库、读请求分散到从库,以提升整体吞吐能力。" },
|
|
54
|
+
"最终一致性": { zh: "最终一致", domain: "系统", gloss: "写入停止后各副本经过一段时间会收敛到相同状态。" },
|
|
55
|
+
"因果一致性": { zh: "因果一致", domain: "系统", gloss: "有因果关系的操作保证顺序可见,无因果的并发操作可任意排序。" },
|
|
56
|
+
"两阶段提交": { zh: "两阶段事务", domain: "系统", gloss: "先让所有参与者表决能否提交,再统一下达提交或回滚指令。" },
|
|
57
|
+
"三阶段提交": { zh: "三阶段事务", domain: "系统", gloss: "在两阶段提交前增加预提交阶段,降低参与者长时间阻塞的概率。" },
|
|
58
|
+
"柔性事务": { zh: "柔性事务", domain: "系统", gloss: "放弃强一致,用重试、补偿等手段让业务最终达到一致的事务方案。" },
|
|
59
|
+
"补偿事务": { zh: "事务补偿", domain: "系统", gloss: "为撤销已成功的步骤而执行反向业务操作,把状态推回可用形态。" },
|
|
60
|
+
"幂等键": { zh: "幂等标识", domain: "系统", gloss: "随请求携带的唯一标识,服务端据此识别并丢弃重复请求。" },
|
|
61
|
+
"令牌桶": { zh: "令牌桶算法", domain: "系统", gloss: "按固定速率往桶里放令牌,取到令牌的请求才被放行,可容忍突发。" },
|
|
62
|
+
"漏桶": { zh: "漏桶算法", domain: "系统", gloss: "请求先入桶再以恒定速率流出,把突发流量整形为平稳输出。" },
|
|
63
|
+
"滑动窗口": { zh: "滑动窗口限流", domain: "系统", gloss: "把统计区间按时间切片并向前滑动,更平滑地限制单位时间请求量。" },
|
|
64
|
+
"布隆过滤器": { zh: "概率判重结构", domain: "计算机", gloss: "用少量内存判断元素是否可能存在,会有假阳性但不会有假阴性。" },
|
|
65
|
+
"跳表": { zh: "多级索引链表", domain: "计算机", gloss: "在有序链表上加多层索引,用随机层数实现近似二分的高效查找。" },
|
|
66
|
+
"倒排索引": { zh: "词到文档映射", domain: "数据", gloss: "记录每个词出现在哪些文档中,是搜索引擎实现快速检索的基础。" },
|
|
67
|
+
"向量检索": { zh: "向量相似搜索", domain: "人工智能", gloss: "把内容编码成向量,按距离或相似度找出最接近的结果。" },
|
|
68
|
+
"召回": { zh: "候选召回", domain: "人工智能", gloss: "从海量内容中快速筛出一批可能相关的候选,供后续精挑细选。" },
|
|
69
|
+
"精排": { zh: "精细排序", domain: "人工智能", gloss: "对少量候选做复杂模型打分,决定最终展示顺序。" },
|
|
70
|
+
"粗排": { zh: "粗粒度排序", domain: "人工智能", gloss: "用较轻的模型从大量候选中快速筛掉明显不合适的部分。" },
|
|
71
|
+
"冷热分离": { zh: "冷热数据分层", domain: "数据", gloss: "把高频访问的热数据与低频冷数据放到不同介质与存储层。" },
|
|
72
|
+
"数据湖": { zh: "原始数据存储池", domain: "数据", gloss: "以原始格式集中存放海量结构与非结构化数据,用时再加工。" },
|
|
73
|
+
"数据仓库": { zh: "分析型数据存储", domain: "数据", gloss: "集成多个业务源、按主题组织并保留历史,专门服务分析查询。" },
|
|
74
|
+
"数据集市": { zh: "部门级数据仓库", domain: "数据", gloss: "面向某个部门或主题从数据仓库裁剪出来的小型分析库。" },
|
|
75
|
+
"宽表": { zh: "宽字段明细表", domain: "数据", gloss: "把多个维度的属性预先拼进一张字段很多的表,减少查询时的连接。" },
|
|
76
|
+
"维度建模": { zh: "维度化建模", domain: "数据", gloss: "把业务过程拆成事实与维度两类表来组织的数仓建模方法。" },
|
|
77
|
+
"指标口径": { zh: "指标统计规则", domain: "数据", gloss: "一个指标如何取数、过滤与聚合的明确定义,避免各算各的。" },
|
|
78
|
+
"口径对齐": { zh: "统一统计口径", domain: "数据", gloss: "让各方对同一指标的算法与边界达成一致,使数字可以互相印证。" },
|
|
79
|
+
"北极星指标": { zh: "核心指引指标", domain: "产品", gloss: "最能代表产品对用户核心价值的那个唯一关键指标。" },
|
|
80
|
+
"漏斗模型": { zh: "分层转化模型", domain: "产品", gloss: "把用户完成目标的路径拆成若干步,逐步统计各步人数与流失。" },
|
|
81
|
+
"AB测试": { zh: "分组对照实验", domain: "产品", gloss: "把用户随机分成几组体验不同方案,用数据比较哪种效果更好。" },
|
|
82
|
+
"大模型": { zh: "大规模预训练模型", domain: "人工智能", gloss: "参数规模巨大、经海量语料预训练、能泛化到多种任务的模型。" },
|
|
83
|
+
"微调": { zh: "模型微调", domain: "人工智能", gloss: "在预训练模型上用特定领域数据继续训练,使其适配具体任务。" },
|
|
84
|
+
"提示词": { zh: "提示文本", domain: "人工智能", gloss: "交给模型的指令与上下文文本,直接决定它输出的方向与质量。" },
|
|
85
|
+
"思维链": { zh: "分步推理链", domain: "人工智能", gloss: "让模型先写出中间推理步骤再给答案,可明显提升复杂问题正确率。" },
|
|
86
|
+
"幻觉": { zh: "模型幻觉", domain: "人工智能", gloss: "模型自信地生成看似合理、实则与事实不符或凭空捏造的内容。" },
|
|
87
|
+
"上下文窗口": { zh: "上下文长度上限", domain: "人工智能", gloss: "模型一次能读取并参与计算的词元数量上限。" },
|
|
88
|
+
"向量数据库": { zh: "向量存储检索库", domain: "人工智能", gloss: "专门存储高维向量并支持相似度检索的数据库。" },
|
|
89
|
+
"知识蒸馏": { zh: "模型知识迁移", domain: "人工智能", gloss: "让小模型学习大模型的输出分布,以更小体量逼近其能力。" },
|
|
90
|
+
"量化": { zh: "低精度压缩", domain: "人工智能", gloss: "把模型权重从高精度压到低比特表示,省显存并加速推理。" },
|
|
91
|
+
"蒸馏": { zh: "教师学生迁移", domain: "人工智能", gloss: "用强模型的输出当软标签去训练小模型,把能力迁移过去。" },
|
|
92
|
+
"推理": { zh: "模型推理", domain: "人工智能", gloss: "模型在训练完成后接收输入并计算输出结果的过程。" },
|
|
93
|
+
"涌现": { zh: "能力突现", domain: "人工智能", gloss: "模型规模跨过某个阈值后,突然出现小模型不具备的新能力。" },
|
|
94
|
+
"参数量": { zh: "模型参数规模", domain: "人工智能", gloss: "模型中可训练权重的总数,通常决定容量、算力与显存需求。" },
|
|
95
|
+
"注意力机制": { zh: "相关性加权机制", domain: "人工智能", gloss: "按相关度为输入各部分动态分配权重并加权汇总的计算方式。" },
|
|
96
|
+
"检索增强": { zh: "检索增强生成", domain: "人工智能", gloss: "先检索外部资料再让模型据此作答,缓解幻觉并注入私有知识。" },
|
|
97
|
+
"降级": { zh: "服务降级", domain: "运维", gloss: "主动关掉次要功能或返回简化结果,把资源留给核心链路。" },
|
|
98
|
+
"削峰填谷": { zh: "流量错峰", domain: "运维", gloss: "用队列把高峰请求暂存起来,等低谷时再慢慢消费掉。" },
|
|
99
|
+
"蓝绿发布": { zh: "双环境切换", domain: "运维", gloss: "同时准备两套环境,验证好新版本后一次性把流量全切过去。" },
|
|
100
|
+
"滚动发布": { zh: "分批滚动升级", domain: "运维", gloss: "分批替换实例,每批确认健康后再继续,实现不停机更新。" },
|
|
101
|
+
"金丝雀发布": { zh: "小流量试发布", domain: "运维", gloss: "先让极少量真实用户使用新版本,指标正常后再逐步扩大范围。" },
|
|
102
|
+
"容量规划": { zh: "资源容量预估", domain: "运维", gloss: "根据业务增长预估所需机器与带宽,提前准备以免临时扩容不及。" },
|
|
103
|
+
"瓶颈": { zh: "性能瓶颈", domain: "系统", gloss: "整条链路中最先撑不住、决定整体上限的那个环节。" },
|
|
104
|
+
"抖动": { zh: "延迟波动", domain: "网络", gloss: "延迟忽高忽低的不稳定现象,会明显伤害实时交互体验。" },
|
|
105
|
+
"毛刺": { zh: "偶发尖刺", domain: "运维", gloss: "监控曲线上短暂而突兀的异常尖峰,通常由瞬时压力引起。" },
|
|
106
|
+
"长尾": { zh: "长尾分布", domain: "数据", gloss: "少数头部之外还有大量单个占比很小但总量可观的部分。" },
|
|
107
|
+
"尾延迟": { zh: "最慢请求耗时", domain: "系统", gloss: "最慢那一小部分请求的响应时间,往往决定用户实际体感。" },
|
|
108
|
+
"死锁": { zh: "互相等待", domain: "软件工程", gloss: "两个以上执行单元各持资源又等对方释放,谁也推进不下去。" },
|
|
109
|
+
"活锁": { zh: "反复重试无进展", domain: "软件工程", gloss: "各方都在不停动作以避免冲突,结果状态始终原地打转。" },
|
|
110
|
+
"饥饿": { zh: "资源得不到分配", domain: "软件工程", gloss: "某个任务长期抢不到资源,始终无法开始或完成执行。" },
|
|
111
|
+
"惊群": { zh: "惊群效应", domain: "系统", gloss: "一个事件唤醒大量进程去抢同一份工作,多数白跑一趟。" },
|
|
112
|
+
"缓存雪崩": { zh: "缓存集体失效", domain: "系统", gloss: "大量缓存同时过期,请求瞬间全部压到数据库上。" },
|
|
113
|
+
"缓存穿透": { zh: "查询不存在数据", domain: "系统", gloss: "反复查询根本不存在的数据,缓存不生效而直接打到后端。" },
|
|
114
|
+
"缓存击穿": { zh: "热点键失效", domain: "系统", gloss: "某个超热门的缓存键过期,大量请求同时回源重建它。" },
|
|
115
|
+
"热点键": { zh: "热点键", domain: "系统", gloss: "访问量远超其他键的单个数据项,容易把承载它的节点打满。" },
|
|
116
|
+
"超大键": { zh: "超大键值", domain: "系统", gloss: "单个键对应的数据体积过大,读写时会阻塞其他请求。" },
|
|
117
|
+
"冷备": { zh: "冷备份", domain: "运维", gloss: "平时不运行、只在主节点故障时手工启用的备份实例。" },
|
|
118
|
+
"热备": { zh: "热备份", domain: "运维", gloss: "与主节点同步运行、可随时接管流量的备用实例。" },
|
|
119
|
+
"双写": { zh: "双份写入", domain: "数据", gloss: "同一次改动同时写入两个存储,用于迁移过渡期保持两边一致。" },
|
|
120
|
+
"主从切换": { zh: "主从角色切换", domain: "系统", gloss: "主节点故障后把某个从节点提升为新的主节点继续服务。" },
|
|
121
|
+
"选主": { zh: "选举主节点", domain: "系统", gloss: "集群在多个候选节点中协商选出唯一主节点来协调写入。" },
|
|
122
|
+
"共识": { zh: "分布式共识", domain: "系统", gloss: "多个节点在可能故障的网络中就某个取值达成一致的机制。" },
|
|
123
|
+
"多数派": { zh: "过半同意", domain: "系统", gloss: "超过半数的节点同意才算通过,任意两个多数派必然有交集。" },
|
|
124
|
+
"法定人数": { zh: "最少同意数", domain: "系统", gloss: "一次决策生效所需的最少同意节点数量。" },
|
|
125
|
+
"事务消息": { zh: "事务型消息", domain: "系统", gloss: "把本地事务与消息发送绑定,保证要么都成功要么都回滚。" },
|
|
126
|
+
"消息积压": { zh: "消费跟不上", domain: "系统", gloss: "生产速度长期高于消费速度,队列中未处理消息越堆越多。" },
|
|
127
|
+
"死信队列": { zh: "失败消息队列", domain: "系统", gloss: "存放多次重试仍失败的消息,避免它们阻塞正常消费流程。" },
|
|
128
|
+
"退避重试": { zh: "指数退避", domain: "系统", gloss: "重试间隔逐次拉长并加入随机抖动,避免把故障服务反复打垮。" },
|
|
129
|
+
"告警收敛": { zh: "告警去噪", domain: "运维", gloss: "把同一故障引发的大量告警合并成少数几条,减少干扰。" },
|
|
130
|
+
"根因分析": { zh: "定位根本原因", domain: "运维", gloss: "穿过表面症状找出真正导致故障的那个底层原因。" },
|
|
131
|
+
"故障演练": { zh: "容灾演练", domain: "运维", gloss: "人为制造故障检验系统的容错与恢复流程是否真的可用。" },
|
|
132
|
+
"应急预案": { zh: "应急处理方案", domain: "运维", gloss: "针对已知重大故障预先写好的处置步骤与责任人分工。" },
|
|
133
|
+
"复盘": { zh: "事后总结", domain: "运维", gloss: "事故后梳理时间线与根因并列出改进项,重点在机制不在追责。" },
|
|
134
|
+
"止损": { zh: "控制损失", domain: "运维", gloss: "故障中优先采取动作阻止影响继续扩大,再谈彻底修复。" },
|
|
135
|
+
"转化率": { zh: "行为转化比例", domain: "产品", gloss: "完成目标行为的用户占进入该环节用户的比例。" },
|
|
136
|
+
"留存率": { zh: "用户留存比例", domain: "产品", gloss: "新增用户在一段时间后仍继续使用的比例,反映产品黏性。" },
|
|
137
|
+
"日活": { zh: "日活跃用户", domain: "产品", gloss: "一天内实际使用过产品的去重用户数量。" },
|
|
138
|
+
"月活": { zh: "月活跃用户", domain: "产品", gloss: "一个月内实际使用过产品的去重用户数量。" },
|
|
139
|
+
"获客成本": { zh: "单个获客花费", domain: "产品", gloss: "获取一个有效新用户平均需要投入的市场与运营费用。" },
|
|
140
|
+
"生命周期价值": { zh: "用户长期价值", domain: "产品", gloss: "一个用户从开始到流失期间为产品带来的全部收益总和。" },
|
|
141
|
+
"复购率": { zh: "重复购买比例", domain: "产品", gloss: "在一定周期内再次购买的用户占已购买用户的比例。" },
|
|
142
|
+
"净推荐值": { zh: "口碑推荐指数", domain: "产品", gloss: "用愿不愿意推荐给他人来衡量用户忠诚度的调研指标。" },
|
|
143
|
+
"用户画像": { zh: "用户特征标签", domain: "数据", gloss: "用一组标签刻画用户的属性、偏好与行为,供精准运营使用。" },
|
|
144
|
+
"标签体系": { zh: "标签分类框架", domain: "数据", gloss: "把用户或内容的标签分层分类组织起来,便于统一生产与使用。" },
|
|
145
|
+
"裂变": { zh: "社交裂变增长", domain: "产品", gloss: "借老用户分享邀请带来新用户,实现低成本的自传播增长。" },
|
|
146
|
+
"私域": { zh: "自有流量池", domain: "产品", gloss: "企业可反复直接触达的用户阵地,不依赖平台分发。" },
|
|
147
|
+
"增长飞轮": { zh: "自增强循环", domain: "产品", gloss: "各环节相互促进形成正反馈,使增长越转越快。" },
|
|
148
|
+
"最小可行产品": { zh: "最小可用版本", domain: "产品", gloss: "只用最少功能验证核心假设、尽快拿到真实反馈的初版产品。" },
|
|
149
|
+
"产品需求文档": { zh: "需求说明文档", domain: "产品", gloss: "描述功能背景、流程与验收标准的文档,供研发与测试对齐。" },
|
|
150
|
+
"用户故事": { zh: "用户视角需求", domain: "软件工程", gloss: "以某类用户想要达成什么目的来表述的一条需求。" },
|
|
151
|
+
"敏捷开发": { zh: "迭代式开发", domain: "软件工程", gloss: "小步快跑、频繁交付并根据反馈持续调整的开发方式。" },
|
|
152
|
+
"冲刺": { zh: "迭代周期", domain: "软件工程", gloss: "敏捷中固定长度的一段开发周期,结束时交付可用增量。" },
|
|
153
|
+
"站会": { zh: "每日站会", domain: "软件工程", gloss: "团队每天短暂同步进展、计划与阻塞的例行会议。" },
|
|
154
|
+
"燃尽图": { zh: "剩余工作量曲线", domain: "软件工程", gloss: "随日期推移展示剩余任务量的图,用来判断迭代是否会延期。" },
|
|
155
|
+
"需求评审": { zh: "需求评审会", domain: "软件工程", gloss: "研发、测试与产品共同确认需求细节与可行性的会议。" },
|
|
156
|
+
"里程碑": { zh: "关键节点", domain: "软件工程", gloss: "项目中被明确标记、用于检查阶段成果的重要时间点。" },
|
|
157
|
+
"热修复": { zh: "线上紧急修复", domain: "运维", gloss: "不重装整包、直接下发补丁修复线上紧急问题的做法。" },
|
|
158
|
+
"版本号": { zh: "版本编号", domain: "软件工程", gloss: "标识软件迭代次序与兼容性的编号规则。" },
|
|
159
|
+
"兼容性": { zh: "版本兼容能力", domain: "软件工程", gloss: "新旧版本之间能否互相正确读取数据与调用接口。" },
|
|
160
|
+
"技术选型": { zh: "技术方案选择", domain: "软件工程", gloss: "在多个候选框架或方案中,结合团队与业务做出取舍决定。" },
|
|
161
|
+
"服务治理": { zh: "服务管控", domain: "系统", gloss: "对微服务的注册、路由、限流与熔断等做统一管理。" },
|
|
162
|
+
"注册中心": { zh: "服务注册表", domain: "系统", gloss: "记录各服务实例地址并感知上下线,供调用方动态发现。" },
|
|
163
|
+
"配置中心": { zh: "集中配置管理", domain: "运维", gloss: "把配置集中存放并可动态下发,改配置无需重新发布。" },
|
|
164
|
+
"服务发现": { zh: "实例自动发现", domain: "系统", gloss: "调用方自动获知可用实例地址而无需硬编码。" },
|
|
165
|
+
"负载均衡": { zh: "请求分发", domain: "网络", gloss: "把请求按策略分给多个后端实例,并剔除不健康的节点。" },
|
|
166
|
+
"反向代理": { zh: "服务端代收转发", domain: "网络", gloss: "站在服务器一侧接收外部请求并转发,隐藏后端拓扑。" },
|
|
167
|
+
"服务降级": { zh: "功能降级", domain: "运维", gloss: "依赖出问题时返回兜底数据或关闭次要功能,保住主流程。" },
|
|
168
|
+
"链路追踪": { zh: "调用链追踪", domain: "运维", gloss: "给一次请求沿途打上标识,串起跨服务调用以定位耗时。" },
|
|
169
|
+
"日志聚合": { zh: "日志集中收集", domain: "运维", gloss: "把分散在各机器上的日志统一采集、索引并提供查询。" },
|
|
170
|
+
"监控大盘": { zh: "指标看板", domain: "运维", gloss: "把关键指标集中画在一屏上,便于快速判断系统健康度。" },
|
|
171
|
+
"弹性伸缩": { zh: "按需扩缩容", domain: "运维", gloss: "依据负载自动增减实例数量,兼顾承载能力与成本。" },
|
|
172
|
+
"容器化": { zh: "应用容器封装", domain: "运维", gloss: "把应用与依赖打包成容器镜像,使环境一致且易于调度。" },
|
|
173
|
+
"无服务器": { zh: "免运维计算", domain: "运维", gloss: "由平台按需分配资源运行代码,使用者不必管理服务器。" },
|
|
174
|
+
"边缘计算": { zh: "近用户计算", domain: "系统", gloss: "把计算与缓存放在离用户更近的节点,降低延迟与回源压力。" },
|
|
175
|
+
"云原生": { zh: "面向云的设计", domain: "运维", gloss: "为容器、微服务与动态编排而设计的一整套架构与实践。" },
|
|
176
|
+
"混合云": { zh: "公私云混用", domain: "运维", gloss: "同时使用自建机房与公有云,按业务特点分配负载。" },
|
|
177
|
+
"私有云": { zh: "专有云环境", domain: "运维", gloss: "由组织独享、部署在自有或托管机房的云平台。" },
|
|
178
|
+
"资源池化": { zh: "资源统一池化", domain: "运维", gloss: "把计算存储资源汇成大池,按需动态分配给不同业务。" },
|
|
179
|
+
"虚拟化": { zh: "硬件资源抽象", domain: "系统", gloss: "用软件把物理资源抽象成多个相互隔离的虚拟执行环境。" },
|
|
180
|
+
"混部": { zh: "在线离线混部", domain: "运维", gloss: "让在线服务与离线任务共用机器,用错峰填谷提升资源利用率。" },
|
|
181
|
+
"数据治理": { zh: "数据管理体系", domain: "数据", gloss: "对数据的标准、质量、安全与权限做制度化管理的总称。" },
|
|
182
|
+
"数据质量": { zh: "数据可信程度", domain: "数据", gloss: "数据在准确性、完整性与及时性上满足使用要求的程度。" },
|
|
183
|
+
"数据血缘": { zh: "数据来源链路", domain: "数据", gloss: "记录一份数据由哪些源表经哪些加工流转而来。" },
|
|
184
|
+
"数据资产": { zh: "可复用数据财富", domain: "数据", gloss: "被登记确权、可被反复使用并产生业务价值的数据。" },
|
|
185
|
+
"元数据": { zh: "描述数据的数据", domain: "数据", gloss: "说明数据的结构、来源、权限与更新时间的附加信息。" },
|
|
186
|
+
"主数据": { zh: "核心基准数据", domain: "数据", gloss: "跨系统共享、描述客户或商品等核心实体的权威数据。" },
|
|
187
|
+
"数据倾斜": { zh: "分布严重不均", domain: "数据", gloss: "少数分片或键承载了绝大部分数据,导致个别任务特别慢。" },
|
|
188
|
+
"拉链表": { zh: "历史状态链表", domain: "数据", gloss: "用起止时间记录每条记录各版本有效期,以保留维度历史。" },
|
|
189
|
+
"全量表": { zh: "全量快照表", domain: "数据", gloss: "每天保存一份完整数据的表,实现简单但存储开销较大。" },
|
|
190
|
+
"增量表": { zh: "增量变更表", domain: "数据", gloss: "只记录自上次以来发生变化的记录,节省存储与计算。" },
|
|
191
|
+
"事实表": { zh: "业务度量表", domain: "数据", gloss: "存放可度量的业务事件及其维度外键的表,是数仓核心。" },
|
|
192
|
+
"维度表": { zh: "分析角度表", domain: "数据", gloss: "存放时间、地区、产品等描述性属性的表,供事实表关联。" },
|
|
193
|
+
"代理键": { zh: "系统生成主键", domain: "数据", gloss: "与业务含义无关的唯一编号,用作维度表的稳定标识。" },
|
|
194
|
+
"事务隔离": { zh: "并发可见性级别", domain: "数据", gloss: "规定并发事务之间能看到彼此哪些改动,权衡一致性与并发度。" },
|
|
195
|
+
"悲观锁": { zh: "先加锁再操作", domain: "数据", gloss: "假定冲突一定会发生,读改之前先锁定资源。" },
|
|
196
|
+
"乐观锁": { zh: "提交时校验版本", domain: "数据", gloss: "平时不加锁,提交时用版本号比对,冲突则回滚重试。" },
|
|
197
|
+
"索引下推": { zh: "条件下推到存储层", domain: "数据", gloss: "把过滤条件下推到索引层提前筛掉数据,减少回表次数。" },
|
|
198
|
+
"执行计划": { zh: "查询执行路径", domain: "数据", gloss: "数据库为一条查询选定的访问方式与算子顺序,可用来诊断性能。" },
|
|
199
|
+
"慢查询": { zh: "耗时过长的查询", domain: "数据", gloss: "执行时间超过阈值的查询,通常是索引缺失或写法不当所致。" },
|
|
200
|
+
"连接池": { zh: "连接复用池", domain: "系统", gloss: "预先建立并复用一批数据库连接,避免频繁建连的开销。" },
|
|
201
|
+
"缓存命中率": { zh: "缓存命中比例", domain: "系统", gloss: "请求在缓存中直接找到结果的比例,越高说明缓存越有效。" },
|
|
202
|
+
"一致性哈希": { zh: "环状哈希分配", domain: "系统", gloss: "把节点与数据映射到同一哈希环,节点增减时只迁移少量数据。" },
|
|
203
|
+
"分布式事务": { zh: "跨节点事务", domain: "系统", gloss: "横跨多个服务或数据库、需要整体成功或整体失败的事务。" },
|
|
204
|
+
"语料": { zh: "训练文本集合", domain: "人工智能", gloss: "用于训练或评估模型的成规模文本、语音等原始数据集合。" },
|
|
205
|
+
"标注": { zh: "人工打标签", domain: "人工智能", gloss: "由人工为样本补充正确答案或结构化信息,形成监督信号。" },
|
|
206
|
+
"训练集": { zh: "模型训练数据", domain: "人工智能", gloss: "用于更新模型参数的那部分带标注数据。" },
|
|
207
|
+
"验证集": { zh: "调参校验数据", domain: "人工智能", gloss: "训练过程中用于选择超参数与早停的独立数据。" },
|
|
208
|
+
"测试集": { zh: "最终评估数据", domain: "人工智能", gloss: "只在最后使用、用来估计真实泛化能力的数据。" },
|
|
209
|
+
"过拟合": { zh: "过度拟合训练集", domain: "人工智能", gloss: "模型连训练数据的噪声都记住了,换到新数据上明显变差。" },
|
|
210
|
+
"欠拟合": { zh: "拟合能力不足", domain: "人工智能", gloss: "模型太简单,连训练数据中的基本规律都没学到。" },
|
|
211
|
+
"泛化": { zh: "迁移到新数据", domain: "人工智能", gloss: "模型在没见过的数据上仍能保持良好表现的能力。" },
|
|
212
|
+
"正则化": { zh: "抑制过拟合手段", domain: "人工智能", gloss: "在目标中加入惩罚或扰动,限制模型复杂度以提升泛化。" },
|
|
213
|
+
"损失函数": { zh: "误差度量函数", domain: "人工智能", gloss: "把预测与正确答案的差距量化为数值,训练就是把它降到最低。" },
|
|
214
|
+
"梯度下降": { zh: "沿梯度调参", domain: "人工智能", gloss: "沿损失下降最快的方向逐步调整参数以逼近最优解。" },
|
|
215
|
+
"反向传播": { zh: "误差反向传递", domain: "人工智能", gloss: "按链式法则从输出层往回计算每层梯度,用于更新参数。" },
|
|
216
|
+
"学习率": { zh: "参数更新步长", domain: "人工智能", gloss: "每次更新参数时迈出的步子大小,过大震荡、过小收敛慢。" },
|
|
217
|
+
"批大小": { zh: "单次样本数量", domain: "人工智能", gloss: "一次前向与反向计算处理的样本数,影响显存与训练稳定性。" },
|
|
218
|
+
"预训练": { zh: "大规模先行训练", domain: "人工智能", gloss: "先在海量通用数据上训练,得到可迁移的基础模型。" },
|
|
219
|
+
"指令微调": { zh: "按指令训练", domain: "人工智能", gloss: "用大量指令与回答数据训练,使模型更愿意按要求完成任务。" },
|
|
220
|
+
"人类反馈": { zh: "人工偏好信号", domain: "人工智能", gloss: "由人比较模型输出优劣所给出的偏好数据,用于对齐训练。" },
|
|
221
|
+
"奖励模型": { zh: "偏好打分模型", domain: "人工智能", gloss: "学习人类偏好、为模型输出打分以指导策略优化的模型。" },
|
|
222
|
+
"越狱": { zh: "绕过安全限制", domain: "人工智能", gloss: "用特殊话术诱导模型突破自身安全策略输出受限内容。" },
|
|
223
|
+
"红队": { zh: "对抗测试队伍", domain: "人工智能", gloss: "主动扮演攻击者去试探模型边界,提前发现安全与合规缺口。" },
|
|
224
|
+
"提示注入": { zh: "恶意指令混入", domain: "人工智能", gloss: "把攻击指令藏进模型读取的文档或网页中,劫持原本任务。" },
|
|
225
|
+
"上下文学习": { zh: "提示内示例学习", domain: "人工智能", gloss: "不更新参数,仅靠提示中给出的示例就让模型学会新任务。" },
|
|
226
|
+
"少样本": { zh: "少量示例提示", domain: "人工智能", gloss: "在提示里只给极少数示例便要求模型完成同类任务。" },
|
|
227
|
+
"思维树": { zh: "多分支推理", domain: "人工智能", gloss: "把推理展开成多条分支并搜索评估,而不是只走一条链。" },
|
|
228
|
+
"智能体": { zh: "自主任务执行体", domain: "人工智能", gloss: "能自主规划、调用工具并根据结果迭代直到完成目标的程序。" },
|
|
229
|
+
"工具调用": { zh: "外部能力调用", domain: "人工智能", gloss: "模型按需调用搜索、计算或代码执行等外部能力来完成任务。" },
|
|
230
|
+
"多智能体": { zh: "多代理协作", domain: "人工智能", gloss: "多个智能体分工协作或相互评审,共同完成复杂任务。" },
|
|
231
|
+
"重排序": { zh: "结果二次排序", domain: "人工智能", gloss: "对初步召回的候选做更精细打分,把最相关的排到前面。" },
|
|
232
|
+
"分块": { zh: "长文本切分", domain: "人工智能", gloss: "把长文档切成大小合适的片段,便于向量化与精准检索。" },
|
|
233
|
+
"嵌入": { zh: "对象向量化", domain: "人工智能", gloss: "把词语、句子或图片映射成稠密向量,使语义相近者距离更近。" },
|
|
234
|
+
"语义检索": { zh: "按含义查找", domain: "人工智能", gloss: "依据向量表示的含义相似度查找内容,而不依赖字面匹配。" },
|
|
235
|
+
"混合检索": { zh: "多路结果融合", domain: "人工智能", gloss: "同时使用关键词与向量检索,再融合两路结果取长补短。" },
|
|
236
|
+
"知识图谱": { zh: "实体关系图", domain: "人工智能", gloss: "用实体与关系构成的图来组织结构化知识,支持推理与查询。" },
|
|
237
|
+
"多模态": { zh: "多种信息形态", domain: "人工智能", gloss: "模型能同时理解文本、图像、音频等多种类型的信息。" },
|
|
238
|
+
"语音识别": { zh: "语音转文字", domain: "人工智能", gloss: "把说话的声音信号自动转换成对应文字的技术。" },
|
|
239
|
+
"语音合成": { zh: "文字转语音", domain: "人工智能", gloss: "把文字转换成自然流畅语音输出的技术。" },
|
|
240
|
+
"光学字符识别": { zh: "图片文字提取", domain: "人工智能", gloss: "从扫描件或照片中自动识别出文字内容的技术。" },
|
|
241
|
+
"命名实体识别": { zh: "专名抽取", domain: "人工智能", gloss: "从文本中自动找出人名、机构、地点等专有实体的任务。" },
|
|
242
|
+
"情感分析": { zh: "情绪倾向判断", domain: "人工智能", gloss: "判断一段文本表达的是正面、负面还是中性情绪。" },
|
|
243
|
+
"机器翻译": { zh: "自动语言转换", domain: "人工智能", gloss: "用模型把一种自然语言自动翻译成另一种语言。" },
|
|
244
|
+
"自动摘要": { zh: "长文压缩", domain: "人工智能", gloss: "把长文本压缩成保留关键信息的短文本,分抽取与生成两类。" },
|
|
245
|
+
"问答系统": { zh: "自动答题", domain: "人工智能", gloss: "根据给定资料或知识库回答用户自然语言提问的系统。" },
|
|
246
|
+
"推荐系统": { zh: "内容自动推荐", domain: "人工智能", gloss: "依据历史行为与相似偏好,为每个用户挑选可能感兴趣的内容。" },
|
|
247
|
+
"协同过滤": { zh: "群体偏好推断", domain: "人工智能", gloss: "利用用户与物品的历史交互矩阵来预测未知偏好。" },
|
|
248
|
+
"点击率": { zh: "曝光点击比例", domain: "产品", gloss: "展示中被用户点击的比例,是推荐与广告排序的核心目标之一。" },
|
|
249
|
+
};
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The overlay surface: the explanation popup, the "add to dictionary"
|
|
5
|
+
* affordance that follows a selection, and the transient notice.
|
|
6
|
+
*
|
|
7
|
+
* All three are fixed-position elements owned by the plugin rather than by the
|
|
8
|
+
* conversation tree, which is why they live in one component: they are one layer
|
|
9
|
+
* over the page and they must not be clipped by the transcript's scroll
|
|
10
|
+
* container. The popup is placed against the pointer and then corrected inward
|
|
11
|
+
* after it has been measured, so it never opens off-screen.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const React = require("react");
|
|
15
|
+
const { applyStyles } = require("./styles.js");
|
|
16
|
+
const { formatStamp } = require("./core.js");
|
|
17
|
+
const { HIGHLIGHT_CSS } = require("./highlight.js");
|
|
18
|
+
|
|
19
|
+
const h = React.createElement;
|
|
20
|
+
|
|
21
|
+
/** Gap between the pointer and the popup's corner. */
|
|
22
|
+
const POINTER_GAP = 10;
|
|
23
|
+
|
|
24
|
+
/** Minimum distance from a viewport edge. */
|
|
25
|
+
const EDGE_GAP = 8;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The stylesheet the annotation layer needs.
|
|
29
|
+
*
|
|
30
|
+
* A `::highlight()` rule only exists in a stylesheet, so it cannot be an inline
|
|
31
|
+
* style like the rest of this file. Rendering it as an element means React removes
|
|
32
|
+
* it with the overlay, so an unloaded plugin leaves neither a rule nor a registry
|
|
33
|
+
* entry behind.
|
|
34
|
+
*
|
|
35
|
+
* @returns the style element.
|
|
36
|
+
*/
|
|
37
|
+
function HighlighterStyle() {
|
|
38
|
+
return h("style", { "data-term-dictionary": "highlight-style" }, HIGHLIGHT_CSS);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The brief explanation that appears when the pointer comes to rest on a collected
|
|
43
|
+
* term. (Removed with the hover gesture — the pointer layer is wikilink's job.)
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The explanation popup, anchored at the pointer.
|
|
48
|
+
*
|
|
49
|
+
* Three states, decided here rather than by the caller: an entry with an
|
|
50
|
+
* explanation, an entry without one (offer to generate), and a term the
|
|
51
|
+
* dictionary has never seen (offer to create).
|
|
52
|
+
*
|
|
53
|
+
* @param props - `term`, `entry`, `context`, `point`, `t`, `pending`, and the
|
|
54
|
+
* `onCreate` / `onGenerate` / `onClose` callbacks.
|
|
55
|
+
* @returns the popup element.
|
|
56
|
+
*/
|
|
57
|
+
function TermPopup(props) {
|
|
58
|
+
const { term, entry, context, point, t, pending } = props;
|
|
59
|
+
const ref = React.useRef(null);
|
|
60
|
+
const [placement, setPlacement] = React.useState(null);
|
|
61
|
+
|
|
62
|
+
// Measure after paint and pull the box back inside the viewport. Doing this in
|
|
63
|
+
// a layout effect keeps the first painted frame in the right place.
|
|
64
|
+
React.useLayoutEffect(() => {
|
|
65
|
+
const element = ref.current;
|
|
66
|
+
if (element === null || point === null || point === undefined) return;
|
|
67
|
+
const rect = element.getBoundingClientRect();
|
|
68
|
+
const viewportWidth = globalThis.innerWidth ?? 0;
|
|
69
|
+
const viewportHeight = globalThis.innerHeight ?? 0;
|
|
70
|
+
let left = point.x + POINTER_GAP;
|
|
71
|
+
let top = point.y + POINTER_GAP;
|
|
72
|
+
if (viewportWidth > 0 && left + rect.width > viewportWidth - EDGE_GAP) left = Math.max(EDGE_GAP, point.x - POINTER_GAP - rect.width);
|
|
73
|
+
if (viewportHeight > 0 && top + rect.height > viewportHeight - EDGE_GAP) top = Math.max(EDGE_GAP, point.y - POINTER_GAP - rect.height);
|
|
74
|
+
setPlacement({ left: Math.round(left), top: Math.round(top) });
|
|
75
|
+
}, [point?.x, point?.y, entry?.id, pending?.phase]);
|
|
76
|
+
|
|
77
|
+
// Dismiss on Escape and on a click outside, which is what every other surface
|
|
78
|
+
// in the page does.
|
|
79
|
+
React.useEffect(() => {
|
|
80
|
+
const onKeyDown = (event) => {
|
|
81
|
+
if (event.key === "Escape") props.onClose();
|
|
82
|
+
};
|
|
83
|
+
const onPointerDown = (event) => {
|
|
84
|
+
const element = ref.current;
|
|
85
|
+
if (element !== null && !element.contains(event.target)) props.onClose();
|
|
86
|
+
};
|
|
87
|
+
document.addEventListener("keydown", onKeyDown);
|
|
88
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
89
|
+
return () => {
|
|
90
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
91
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
92
|
+
};
|
|
93
|
+
}, [props.onClose]);
|
|
94
|
+
|
|
95
|
+
const gloss = entry?.definition?.gloss ?? "";
|
|
96
|
+
const style = {
|
|
97
|
+
...applyStyles.popup,
|
|
98
|
+
...(placement === null ? { left: "-9999px", top: "-9999px" } : { left: `${placement.left}px`, top: `${placement.top}px` })
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return h(
|
|
102
|
+
"div",
|
|
103
|
+
{ ref, style, role: "dialog", "aria-label": term, "data-term-dictionary": "popup" },
|
|
104
|
+
h(
|
|
105
|
+
"div",
|
|
106
|
+
{ style: applyStyles.popupHead },
|
|
107
|
+
h("p", { style: applyStyles.popupTerm }, term),
|
|
108
|
+
entry !== null && entry.definition.zh !== "" ? h("span", { style: applyStyles.popupZh }, entry.definition.zh) : null
|
|
109
|
+
),
|
|
110
|
+
gloss !== ""
|
|
111
|
+
? h("p", { style: applyStyles.popupGloss }, gloss)
|
|
112
|
+
: entry !== null
|
|
113
|
+
? h("p", { style: applyStyles.popupGloss }, t("noDefinition"))
|
|
114
|
+
: h("p", { style: applyStyles.popupGloss }, t("popupUnknown")),
|
|
115
|
+
entry !== null && entry.definition.usage !== "" ? h("p", { style: applyStyles.popupUsage }, entry.definition.usage) : null,
|
|
116
|
+
entry === null && context !== "" ? h("p", { style: applyStyles.popupContext }, context) : null,
|
|
117
|
+
pending?.phase === "running" ? h("p", { style: applyStyles.popupMeta }, t("popupGenerating")) : null,
|
|
118
|
+
pending?.phase === "failed" ? h("p", { style: applyStyles.error }, t("generateFailed", { message: pending.message })) : null,
|
|
119
|
+
h(
|
|
120
|
+
"div",
|
|
121
|
+
{ style: applyStyles.popupActions },
|
|
122
|
+
gloss === ""
|
|
123
|
+
? h(
|
|
124
|
+
"button",
|
|
125
|
+
{
|
|
126
|
+
type: "button",
|
|
127
|
+
style: applyStyles.primaryButton,
|
|
128
|
+
disabled: pending?.phase === "running",
|
|
129
|
+
onClick: props.onGenerate
|
|
130
|
+
},
|
|
131
|
+
pending?.phase === "running" ? t("generating") : t("generate")
|
|
132
|
+
)
|
|
133
|
+
: null,
|
|
134
|
+
h(
|
|
135
|
+
"button",
|
|
136
|
+
{ type: "button", style: applyStyles.button, onClick: props.onCreate },
|
|
137
|
+
entry === null ? t("popupCreate") : t("edit")
|
|
138
|
+
),
|
|
139
|
+
h("span", { style: applyStyles.spacer }),
|
|
140
|
+
entry !== null
|
|
141
|
+
? h(
|
|
142
|
+
"span",
|
|
143
|
+
{ style: applyStyles.popupMeta },
|
|
144
|
+
[sourceLabel(entry.source, t), entry.domain, entry.seen > 0 ? t("seenTimes", { count: entry.seen }) : ""].filter((part) => part !== "").join(" · ")
|
|
145
|
+
)
|
|
146
|
+
: null
|
|
147
|
+
)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Localized label for an entry's provenance. */
|
|
152
|
+
function sourceLabel(source, t) {
|
|
153
|
+
switch (source) {
|
|
154
|
+
case "user":
|
|
155
|
+
return t("popupSourceUser");
|
|
156
|
+
case "llm":
|
|
157
|
+
return t("popupSourceLlm");
|
|
158
|
+
case "heuristic":
|
|
159
|
+
return t("popupSourceHeuristic");
|
|
160
|
+
case "import":
|
|
161
|
+
return t("popupSourceImport");
|
|
162
|
+
default:
|
|
163
|
+
return t("popupSourceAuto");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* The small button that appears beside a selection inside a reply.
|
|
169
|
+
*
|
|
170
|
+
* @param props - `selection` (`{ term, rect }`), `label`, `title`, `onAdd`, `onClose`.
|
|
171
|
+
* @returns the affordance element.
|
|
172
|
+
*/
|
|
173
|
+
function SelectionAffordance(props) {
|
|
174
|
+
const { selection, label, title } = props;
|
|
175
|
+
const ref = React.useRef(null);
|
|
176
|
+
const [position, setPosition] = React.useState(null);
|
|
177
|
+
|
|
178
|
+
React.useLayoutEffect(() => {
|
|
179
|
+
const element = ref.current;
|
|
180
|
+
if (element === null || selection?.rect === undefined) return;
|
|
181
|
+
const rect = element.getBoundingClientRect();
|
|
182
|
+
const viewportWidth = globalThis.innerWidth ?? 0;
|
|
183
|
+
const viewportHeight = globalThis.innerHeight ?? 0;
|
|
184
|
+
const anchor = selection.rect;
|
|
185
|
+
// Prefer below the selection, above it when the page bottom is close.
|
|
186
|
+
let top = anchor.bottom + 6;
|
|
187
|
+
if (viewportHeight > 0 && top + rect.height > viewportHeight - EDGE_GAP) top = Math.max(EDGE_GAP, anchor.top - rect.height - 6);
|
|
188
|
+
let left = anchor.left;
|
|
189
|
+
if (viewportWidth > 0 && left + rect.width > viewportWidth - EDGE_GAP) left = Math.max(EDGE_GAP, viewportWidth - rect.width - EDGE_GAP);
|
|
190
|
+
setPosition({ left: Math.round(left), top: Math.round(top) });
|
|
191
|
+
}, [selection?.term, selection?.rect?.top, selection?.rect?.bottom, selection?.rect?.left]);
|
|
192
|
+
|
|
193
|
+
return h(
|
|
194
|
+
"button",
|
|
195
|
+
{
|
|
196
|
+
ref,
|
|
197
|
+
type: "button",
|
|
198
|
+
title,
|
|
199
|
+
// The marker lets the transcript's document-level `mousedown` listener tell a
|
|
200
|
+
// press on this button from a press that abandons the selection. Without it the
|
|
201
|
+
// button was unmounted before its own `click` could run.
|
|
202
|
+
"data-term-dictionary": "affordance",
|
|
203
|
+
style: { ...applyStyles.selectionButton, ...(position === null ? { left: "-9999px", top: "-9999px" } : { left: `${position.left}px`, top: `${position.top}px` }) },
|
|
204
|
+
// A pointerdown inside the affordance would collapse the selection before
|
|
205
|
+
// the click lands, so the selection is preserved deliberately.
|
|
206
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
207
|
+
onClick: props.onAdd
|
|
208
|
+
},
|
|
209
|
+
h("span", { "aria-hidden": "true" }, "+"),
|
|
210
|
+
h("span", null, label)
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* The transient notice shown after an automatic collection.
|
|
216
|
+
*
|
|
217
|
+
* The notice itself is `pointer-events: none` (its style) so that it cannot swallow clicks meant for
|
|
218
|
+
* whatever it happens to be covering — and it covers the composer, being bottom-centre with the
|
|
219
|
+
* highest z-index. Its buttons opt back IN, because they are the only part that is meant to be
|
|
220
|
+
* pressed.
|
|
221
|
+
*
|
|
222
|
+
* @param props - `text`, optional `actionLabel` and `onAction`, `onClose`.
|
|
223
|
+
* @returns the notice element.
|
|
224
|
+
*/
|
|
225
|
+
function Toast(props) {
|
|
226
|
+
/** The interactive parts of a notice that otherwise lets pointers through. */
|
|
227
|
+
const buttonStyle = { ...applyStyles.linkButton, pointerEvents: "auto" };
|
|
228
|
+
return h(
|
|
229
|
+
"div",
|
|
230
|
+
{ style: applyStyles.toast, role: "status", "data-term-dictionary": "toast" },
|
|
231
|
+
h("span", null, props.text),
|
|
232
|
+
props.actionLabel !== null && props.actionLabel !== undefined && typeof props.onAction === "function"
|
|
233
|
+
? h("button", { type: "button", style: buttonStyle, onClick: props.onAction }, props.actionLabel)
|
|
234
|
+
: null,
|
|
235
|
+
h("button", { type: "button", style: buttonStyle, onClick: props.onClose, "aria-label": "close" }, "✕")
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
module.exports = { TermPopup, SelectionAffordance, Toast, HighlighterStyle, sourceLabel };
|