From d61d014a19cf8fd4fbf6eabed661ccbb64ece338 Mon Sep 17 00:00:00 2001 From: bowenliang123 Date: Tue, 29 Sep 2026 01:57:52 +0800 Subject: [PATCH] chore(lint): enforce one object property per line Add @stylistic/object-property-newline to the src stylistic block, so an object that already spans multiple lines cannot pack several properties onto one line. Objects written entirely on one line stay compact, which keeps small inline props objects untouched. The autofix split 199 packed properties across i18n.ts, statsTiming.tsx, fileActivity.ts, index.ts, fold.ts and imageTokens.ts. Note that this rule's fixer only inserts the newlines, so @stylistic/indent repairs the resulting indentation on a second lint:fix pass. --- .oxlintrc.json | 6 + src/client/components/statsTiming.tsx | 10 +- src/client/fileActivity.ts | 8 +- src/client/i18n.ts | 384 +++++++++++++++++++------- src/client/index.ts | 5 +- src/host/fold.ts | 11 +- src/shared/imageTokens.ts | 5 +- 7 files changed, 320 insertions(+), 109 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index bb16b9a..bb94f66 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -194,6 +194,12 @@ "error", "always" ], + "@stylistic/object-property-newline": [ + "error", + { + "allowAllPropertiesOnSameLine": true + } + ], "@stylistic/arrow-parens": [ "error", "as-needed", diff --git a/src/client/components/statsTiming.tsx b/src/client/components/statsTiming.tsx index 86b7b68..731216e 100644 --- a/src/client/components/statsTiming.tsx +++ b/src/client/components/statsTiming.tsx @@ -145,7 +145,10 @@ export function makeStatsTiming(kit: ViewKit, Donut: (props: DonutProps) => Reac key: slice.key, color: slice.color, value: share(modelRings[index]), })) const toolSlice: Slice = { - key: 'tools', color: COLOR.tools, label: t('timing.tools'), ms: timing.toolsMs, + key: 'tools', + color: COLOR.tools, + label: t('timing.tools'), + ms: timing.toolsMs, times: t('timing.toolTimes', { n: fmt(timing.toolCalls) }), } const otherSlice: Slice = { key: 'other', color: COLOR.other, label: t('timing.other'), ms: other } @@ -155,7 +158,10 @@ export function makeStatsTiming(kit: ViewKit, Donut: (props: DonutProps) => Reac { key: 'other', color: COLOR.other, value: share(other) }, ] const toRow = (slice: Slice): SliceRow => ({ - key: slice.key, color: slice.color, label: slice.label, dim: slice.ms === 0, + key: slice.key, + color: slice.color, + label: slice.label, + dim: slice.ms === 0, pct: fmtShare(slice.ms, wall), count: countOf(slice.ms, slice.times), }) diff --git a/src/client/fileActivity.ts b/src/client/fileActivity.ts index 8d07fe6..2244333 100644 --- a/src/client/fileActivity.ts +++ b/src/client/fileActivity.ts @@ -274,8 +274,12 @@ function aggregateOps(ops: readonly FileOp[], before: number | null): FileActivi entry = { path: op.path, form: formOf(op.tool, op.path), - reads: 0, writes: 0, searches: 0, - added: 0, removed: 0, errs: 0, + reads: 0, + writes: 0, + searches: 0, + added: 0, + removed: 0, + errs: 0, ops: [], ...(op.pattern === true ? { pattern: true as const } : {}), } diff --git a/src/client/i18n.ts b/src/client/i18n.ts index 8fca6ff..2472442 100644 --- a/src/client/i18n.ts +++ b/src/client/i18n.ts @@ -8,8 +8,13 @@ export type Translate = (key: string, params?: Record) export const DICT_ZH: Record = { 'tab': '上下文', 'sidebar.guideDescription': '查看上下文的构成、统计、演变。', - 'cat.system': '系统提示词', 'cat.tools': '工具定义', 'cat.user': '用户消息', - 'cat.inject': '注入内容', 'cat.skill': '技能注入', 'cat.assistant': '助手消息', 'cat.tool': '工具结果', + 'cat.system': '系统提示词', + 'cat.tools': '工具定义', + 'cat.user': '用户消息', + 'cat.inject': '注入内容', + 'cat.skill': '技能注入', + 'cat.assistant': '助手消息', + 'cat.tool': '工具结果', 'overview.title': '当前上下文', 'overview.estimate': 'tokens(估算)', 'overview.free': '剩余窗口', @@ -17,7 +22,8 @@ export const DICT_ZH: Record = { 'overview.ofUsed': '占已用上下文', 'overview.compactReserve': '自动压缩预留:占用达 {pct}% 窗口时触发压缩,此区域一般不实际占用', 'stats.title': '上下文统计', - 'stats.turns': '轮次', 'stats.steps': '步数', + 'stats.turns': '轮次', + 'stats.steps': '步数', 'stats.humanInputs': '用户输入', 'stats.humanInputsTip': '当前会话中的用户消息与问题回答次数。', 'stats.toolCalls': '工具调用', @@ -31,7 +37,10 @@ export const DICT_ZH: Record = { 'stats.subCostTip': '所有子 Agent 会话各自累计费用(同口径估算)的合计,不含当前 Agent 自身。', 'stats.costPriceHead': '每百万 Token 价格:', 'stats.costPriceBy': '按 {p} · {m} 在 models.dev 的刊登价格如上。', - 'stats.costHit': '缓存输入', 'stats.costMiss': '未缓存输入', 'stats.costWrite': '缓存写入', 'stats.costOut': '输出', + 'stats.costHit': '缓存输入', + 'stats.costMiss': '未缓存输入', + 'stats.costWrite': '缓存写入', + 'stats.costOut': '输出', 'stats.costUnavailable': '模型价格暂不可用(自动重试中),费用暂无法估算。', 'timing.title': '耗时统计', 'timing.total': '活跃时长', @@ -42,7 +51,9 @@ export const DICT_ZH: Record = { 'timing.toolArgs': '工具参数', 'timing.tools': '工具执行', 'timing.other': '其他开销', - 'timing.callTimes': '{n}次', 'timing.toolTimes': '{n}次', 'timing.blockTimes': '{n}次', + 'timing.callTimes': '{n}次', + 'timing.toolTimes': '{n}次', + 'timing.blockTimes': '{n}次', 'timing.empty': '暂无耗时数据 · 对话开始后这里会显示时长分布', 'timing.tps': '{tps} tok/s', 'timing.tpsTip': '输出速度(TPS):累计解码输出 tokens ÷ 累计解码时长,与 DeepSeek Harness 会话统计同口径。', @@ -57,19 +68,26 @@ export const DICT_ZH: Record = { 'plugin.settings': '设置', 'plugin.settingsOpen': '打开插件设置', 'trend.title': '上下文趋势', - 'gran.step': '步骤', 'gran.turn': '轮次', - 'settings.title': '上下文', 'settings.desc': 'dsh-context 插件中上下文面板的偏好设置', + 'gran.step': '步骤', + 'gran.turn': '轮次', + 'settings.title': '上下文', + 'settings.desc': 'dsh-context 插件中上下文面板的偏好设置', 'settings.placement': '入口位置', - 'placement.tab': '标签页', 'placement.sidebar': '侧边栏', 'placement.all': '全部', + 'placement.tab': '标签页', + 'placement.sidebar': '侧边栏', + 'placement.all': '全部', 'settings.insightsEntry': '上下文洞察入口', - 'insightsEntry.show': '显示', 'insightsEntry.hide': '隐藏', + 'insightsEntry.show': '显示', + 'insightsEntry.hide': '隐藏', 'settings.gran': '趋势图 · 默认粒度', 'settings.mode': '趋势图 · 默认展示方式', 'settings.toolSort': '工具定义 · 默认排序', 'settings.fileSort': '文件活动 · 默认排序', - 'settings.expand': '展开', 'settings.collapse': '收起', + 'settings.expand': '展开', + 'settings.collapse': '收起', 'settings.readOnly': '当前环境的设置为只读', - 'gran.total': '全量', 'gran.delta': '增量', + 'gran.total': '全量', + 'gran.delta': '增量', 'gran.modeHint': '全量:累计构成;增量:相对上一条请求的变化量', 'trend.focus': '仅展示 {cat}', 'trend.adaptive': '自适应', @@ -97,14 +115,23 @@ export const DICT_ZH: Record = { 'events.at': '第 {t} 轮 · 第 {s} 步', 'events.range': '第 {t} 轮 · 第 {a}→{b} 步', 'events.rangeTo': '第 {a} 轮 · 第 {as} 步 → 第 {b} 轮 · 第 {bs} 步', - 'kind.inject': '注入', 'kind.compaction': '压缩', 'kind.prune': '剪枝', 'kind.model': '切换', 'kind.mode': '模式', + 'kind.inject': '注入', + 'kind.compaction': '压缩', + 'kind.prune': '剪枝', + 'kind.model': '切换', + 'kind.mode': '模式', 'files.title': '文件活动', 'files.scopeLatest': '截至最新 · 跟随趋势图的选择', - 'files.kind.all': '全部', 'files.kind.read': '读取', 'files.kind.write': '写入', - 'files.kind.search': '搜索', 'files.kind.image': '图片', + 'files.kind.all': '全部', + 'files.kind.read': '读取', + 'files.kind.write': '写入', + 'files.kind.search': '搜索', + 'files.kind.image': '图片', 'files.chipTip': '{files} 个文件 · {ops} 次操作', 'files.search': '按路径过滤…', - 'files.sort.count': '按次数', 'files.sort.latest': '按最新', 'files.sort.path': '按路径', + 'files.sort.count': '按次数', + 'files.sort.latest': '按最新', + 'files.sort.path': '按路径', 'files.sortTip': '文件排序:按操作次数(选中分类时按该分类的次数)、最近操作时间或路径', 'files.files': '{n} 个文件', 'files.deltaTip': '按 edit/write 调用参数估算的累计行数变化:+ 新增 / − 删除', @@ -117,23 +144,53 @@ export const DICT_ZH: Record = { 'files.preview': '在侧边栏预览', 'files.readEst': '按调用参数 limit 估算的读取行数', 'files.errs': '{n} 次失败', - 'files.form.image': '图片', 'files.form.dir': '目录', 'files.form.text': '文件', - 'files.glyph.root': '工作区根', 'files.glyph.tests': '测试', 'files.glyph.docs': '文档目录', - 'files.glyph.deps': '依赖', 'files.glyph.build': '构建产物', 'files.glyph.scripts': '脚本', - 'files.glyph.config': '配置', 'files.glyph.assets': '静态资源', 'files.glyph.hidden': '元数据目录', - 'files.glyph.lock': '锁文件', 'files.glyph.docker': 'Docker', 'files.glyph.ignore': '忽略规则', - 'files.glyph.license': '许可证', 'files.glyph.python': 'Python', 'files.glyph.notebook': 'Notebook', - 'files.glyph.shell': 'Shell', 'files.glyph.style': '样式', - 'files.glyph.database': '数据库', 'files.glyph.data': '数据清单', - 'files.glyph.markdown': '标记文档', 'files.glyph.log': '日志', 'files.glyph.sheet': '表格', - 'files.glyph.document': '文档', 'files.glyph.archive': '压缩包', 'files.glyph.font': '字体', + 'files.form.image': '图片', + 'files.form.dir': '目录', + 'files.form.text': '文件', + 'files.glyph.root': '工作区根', + 'files.glyph.tests': '测试', + 'files.glyph.docs': '文档目录', + 'files.glyph.deps': '依赖', + 'files.glyph.build': '构建产物', + 'files.glyph.scripts': '脚本', + 'files.glyph.config': '配置', + 'files.glyph.assets': '静态资源', + 'files.glyph.hidden': '元数据目录', + 'files.glyph.lock': '锁文件', + 'files.glyph.docker': 'Docker', + 'files.glyph.ignore': '忽略规则', + 'files.glyph.license': '许可证', + 'files.glyph.python': 'Python', + 'files.glyph.notebook': 'Notebook', + 'files.glyph.shell': 'Shell', + 'files.glyph.style': '样式', + 'files.glyph.database': '数据库', + 'files.glyph.data': '数据清单', + 'files.glyph.markdown': '标记文档', + 'files.glyph.log': '日志', + 'files.glyph.sheet': '表格', + 'files.glyph.document': '文档', + 'files.glyph.archive': '压缩包', + 'files.glyph.font': '字体', 'files.glyph.media': '媒体', - 'files.glyph.lang.ts': 'TypeScript', 'files.glyph.lang.js': 'JavaScript', 'files.glyph.lang.go': 'Go', - 'files.glyph.lang.rust': 'Rust', 'files.glyph.lang.java': 'Java', 'files.glyph.lang.kotlin': 'Kotlin', - 'files.glyph.lang.ruby': 'Ruby', 'files.glyph.lang.php': 'PHP', 'files.glyph.lang.c': 'C', - 'files.glyph.lang.cpp': 'C++', 'files.glyph.lang.csharp': 'C#', 'files.glyph.lang.scala': 'Scala', - 'files.glyph.lang.lua': 'Lua', 'files.glyph.lang.dart': 'Dart', 'files.glyph.lang.swift': 'Swift', - 'files.glyph.lang.vue': 'Vue', 'files.glyph.lang.svelte': 'Svelte', 'files.glyph.lang.html': 'HTML', + 'files.glyph.lang.ts': 'TypeScript', + 'files.glyph.lang.js': 'JavaScript', + 'files.glyph.lang.go': 'Go', + 'files.glyph.lang.rust': 'Rust', + 'files.glyph.lang.java': 'Java', + 'files.glyph.lang.kotlin': 'Kotlin', + 'files.glyph.lang.ruby': 'Ruby', + 'files.glyph.lang.php': 'PHP', + 'files.glyph.lang.c': 'C', + 'files.glyph.lang.cpp': 'C++', + 'files.glyph.lang.csharp': 'C#', + 'files.glyph.lang.scala': 'Scala', + 'files.glyph.lang.lua': 'Lua', + 'files.glyph.lang.dart': 'Dart', + 'files.glyph.lang.swift': 'Swift', + 'files.glyph.lang.vue': 'Vue', + 'files.glyph.lang.svelte': 'Svelte', + 'files.glyph.lang.html': 'HTML', 'agents.title': 'Agent 网络', 'agents.sub': '当前 Agent 与子 Agent 的实时上下文 · 点击节点跳转会话', 'agents.chip.count': '{n} 个 Agent', @@ -171,9 +228,15 @@ export const DICT_ZH: Record = { 'ev.prune': '剪枝工具输出', 'ev.skill': 'Skill 注入({name})', 'ev.model': '模型切换:{a} → {b}', - 'ev.mode.plan.on': '进入计划模式', 'ev.mode.plan.off': '退出计划模式', - 'form.instructions': '指令注入', 'form.catalog': '目录更新', 'form.snapshot': '状态快照', - 'form.notice': '通知', 'form.relay': '代理转发', 'form.recall': '历史召回', 'form.context': '上下文注入', + 'ev.mode.plan.on': '进入计划模式', + 'ev.mode.plan.off': '退出计划模式', + 'form.instructions': '指令注入', + 'form.catalog': '目录更新', + 'form.snapshot': '状态快照', + 'form.notice': '通知', + 'form.relay': '代理转发', + 'form.recall': '历史召回', + 'form.context': '上下文注入', 'node.toolResult': '工具结果', 'node.calls': '调用 ', 'node.empty': '(空回复)', @@ -203,7 +266,9 @@ export const DICT_ZH: Record = { 'browser.search.skill': '按技能过滤…', 'browser.search.assistant': '按回复或调用过滤…', 'browser.search.tool': '按工具或参数过滤…', - 'browser.kind.think': '思考', 'browser.kind.tool': '工具', 'browser.kind.answer': '回答', + 'browser.kind.think': '思考', + 'browser.kind.tool': '工具', + 'browser.kind.answer': '回答', 'browser.kindTip': '按内容种类过滤回复:思考、工具调用、回答文本', 'browser.rowNoMatch': '没有匹配当前过滤条件的行', 'browser.loading': '正在从更早的会话历史加载完整内容…', @@ -218,7 +283,9 @@ export const DICT_ZH: Record = { 'tool.jsonToggle': '查看原始 JSON', 'tool.jsonHide': '收起', 'tool.search': '按名称、描述或参数过滤…', - 'tool.sort.size': '按大小', 'tool.sort.count': '按次数', 'tool.sort.name': '按名称', + 'tool.sort.size': '按大小', + 'tool.sort.count': '按次数', + 'tool.sort.name': '按名称', 'tool.sortTip': '工具排序:按当前步骤中的调用次数(默认)、token 大小或名称', 'tool.hitsTip': '当前展示步骤的上下文中,该工具被调用并返回结果的次数', 'tool.noMatch': '没有匹配当前过滤条件的工具', @@ -226,16 +293,23 @@ export const DICT_ZH: Record = { 'rich.md': 'Markdown', 'rich.toMd': '按 Markdown 渲染查看', 'rich.toRaw': '查看原始文本', - 'rich.copy': '复制原文', 'rich.copied': '已复制', - 'rich.md.copy': '复制', 'rich.md.copied': '已复制', + 'rich.copy': '复制原文', + 'rich.copied': '已复制', + 'rich.md.copy': '复制', + 'rich.md.copied': '已复制', 'rich.md.footnotes': '脚注', 'block.thinking': '思考', 'block.answer': '回答', - 'block.content': '内容', 'block.result': '结果', 'block.summary': '摘要', + 'block.content': '内容', + 'block.result': '结果', + 'block.summary': '摘要', // Line-count chip on a tool-result's payload card (the singular/plural // split is a no-op in zh, where the measure word never inflects). - 'block.line': '{n} 行', 'block.lines': '{n} 行', - 'call.ok': '正常', 'call.fail': '失败', 'call.exit': 'exit {n}', + 'block.line': '{n} 行', + 'block.lines': '{n} 行', + 'call.ok': '正常', + 'call.fail': '失败', + 'call.exit': 'exit {n}', 'node.failed': '工具执行失败', 'attach.images': '图片附件', 'attach.other': '其他内容', @@ -253,7 +327,10 @@ export const DICT_ZH: Record = { 'ov.title': '上下文洞察', 'ov.unavailable': '会话列表暂不可用', 'ov.range.label': '统计范围', - 'ov.range.24h': '近 24 小时', 'ov.range.7d': '近 7 天', 'ov.range.30d': '近 30 天', 'ov.range.all': '全部', + 'ov.range.24h': '近 24 小时', + 'ov.range.7d': '近 7 天', + 'ov.range.30d': '近 30 天', + 'ov.range.all': '全部', 'ov.kpi.sessions': '活跃会话', 'ov.kpi.ofTotal': '共 {n} 个', 'ov.kpi.tokens': '累计消耗', @@ -274,18 +351,33 @@ export const DICT_ZH: Record = { 'ov.heat.sub': '按日期筛选', 'ov.heat.empty': '暂无活跃数据 — 会话产生模型请求后,这里会显示每日用量', 'ov.heat.sessions': '{n} 个活跃会话', - 'ov.heat.less': '少', 'ov.heat.more': '多', - 'ov.heat.metric': '指标', 'ov.heat.metric.sessions': '会话', 'ov.heat.metric.steps': '步数', + 'ov.heat.less': '少', + 'ov.heat.more': '多', + 'ov.heat.metric': '指标', + 'ov.heat.metric.sessions': '会话', + 'ov.heat.metric.steps': '步数', 'ov.heat.steps': '{n} 步', - 'ov.heat.wd.1': '星期一', 'ov.heat.wd.3': '星期三', 'ov.heat.wd.5': '星期五', - 'ov.heat.mon.01': '1月', 'ov.heat.mon.02': '2月', 'ov.heat.mon.03': '3月', - 'ov.heat.mon.04': '4月', 'ov.heat.mon.05': '5月', 'ov.heat.mon.06': '6月', - 'ov.heat.mon.07': '7月', 'ov.heat.mon.08': '8月', 'ov.heat.mon.09': '9月', - 'ov.heat.mon.10': '10月', 'ov.heat.mon.11': '11月', 'ov.heat.mon.12': '12月', + 'ov.heat.wd.1': '星期一', + 'ov.heat.wd.3': '星期三', + 'ov.heat.wd.5': '星期五', + 'ov.heat.mon.01': '1月', + 'ov.heat.mon.02': '2月', + 'ov.heat.mon.03': '3月', + 'ov.heat.mon.04': '4月', + 'ov.heat.mon.05': '5月', + 'ov.heat.mon.06': '6月', + 'ov.heat.mon.07': '7月', + 'ov.heat.mon.08': '8月', + 'ov.heat.mon.09': '9月', + 'ov.heat.mon.10': '10月', + 'ov.heat.mon.11': '11月', + 'ov.heat.mon.12': '12月', 'ov.list.title': '会话', 'ov.list.search': '搜索标题 / 目录 / 最后消息…', 'ov.list.sortLabel': '排序', - 'ov.list.sort.recent': '最近更新', 'ov.list.sort.tokens': 'Token 总用量', 'ov.list.sort.context': '当前 Context', + 'ov.list.sort.recent': '最近更新', + 'ov.list.sort.tokens': 'Token 总用量', + 'ov.list.sort.context': '当前 Context', 'ov.list.empty': '还没有会话 — 发起一轮对话后,这里会列出每个会话的上下文画像', 'ov.list.noMatch': '没有匹配当前筛选条件的会话', 'ov.list.noData': '暂无上下文数据 — 该会话产生模型请求后才会统计', @@ -310,8 +402,13 @@ export const DICT_ZH: Record = { export const DICT_EN: Record = { 'tab': 'Context', 'sidebar.guideDescription': 'Inspect context composition, stats, and evolution.', - 'cat.system': 'System Prompt', 'cat.tools': 'Tool Schemas', 'cat.user': 'User Messages', - 'cat.inject': 'Injected Context', 'cat.skill': 'Skill Injections', 'cat.assistant': 'Assistant Messages', 'cat.tool': 'Tool Results', + 'cat.system': 'System Prompt', + 'cat.tools': 'Tool Schemas', + 'cat.user': 'User Messages', + 'cat.inject': 'Injected Context', + 'cat.skill': 'Skill Injections', + 'cat.assistant': 'Assistant Messages', + 'cat.tool': 'Tool Results', 'overview.title': 'Current Context', 'overview.estimate': 'tokens (estimated)', 'overview.free': 'Free Window', @@ -319,7 +416,8 @@ export const DICT_EN: Record = { 'overview.ofUsed': 'of used context', 'overview.compactReserve': 'Auto-compaction reserve: auto-compaction triggers at {pct}% of the window — this area is normally kept as headroom, not actually used', 'stats.title': 'Context Stats', - 'stats.turns': 'Turns', 'stats.steps': 'Steps', + 'stats.turns': 'Turns', + 'stats.steps': 'Steps', 'stats.humanInputs': 'Human Inputs', 'stats.humanInputsTip': 'User messages and question answerings in current session.', 'stats.toolCalls': 'Tool Calls', @@ -333,7 +431,10 @@ export const DICT_EN: Record = { 'stats.subCostTip': 'Summed estimates of every subagent session’s own cumulative cost (same basis); the current agent is not included.', 'stats.costPriceHead': 'Per-1M-token rates:', 'stats.costPriceBy': 'Priced as listed on models.dev for {p} · {m}.', - 'stats.costHit': 'Cache input', 'stats.costMiss': 'Uncached input', 'stats.costWrite': 'Cache write', 'stats.costOut': 'Output', + 'stats.costHit': 'Cache input', + 'stats.costMiss': 'Uncached input', + 'stats.costWrite': 'Cache write', + 'stats.costOut': 'Output', 'stats.costUnavailable': 'Model prices are unavailable (retrying automatically) — the cost cannot be estimated yet.', 'timing.title': 'Timing Stats', 'timing.total': 'Active Time', @@ -344,7 +445,9 @@ export const DICT_EN: Record = { 'timing.toolArgs': 'Tool args', 'timing.tools': 'Tool runs', 'timing.other': 'Overhead', - 'timing.callTimes': '{n} calls', 'timing.toolTimes': '{n} runs', 'timing.blockTimes': '{n} blocks', + 'timing.callTimes': '{n} calls', + 'timing.toolTimes': '{n} runs', + 'timing.blockTimes': '{n} blocks', 'timing.empty': 'No timing data yet — durations appear as the conversation runs', 'timing.tps': '{tps} tok/s', 'timing.tpsTip': 'Output speed (TPS): summed decoded output tokens over summed decode time — the same pairing as the harness session stats.', @@ -359,19 +462,26 @@ export const DICT_EN: Record = { 'plugin.settings': 'Settings', 'plugin.settingsOpen': 'Open plugin settings', 'trend.title': 'Context Trend', - 'gran.step': 'Step', 'gran.turn': 'Turn', - 'settings.title': 'Context', 'settings.desc': 'Preferences for Context panel in dsh-context plugin', + 'gran.step': 'Step', + 'gran.turn': 'Turn', + 'settings.title': 'Context', + 'settings.desc': 'Preferences for Context panel in dsh-context plugin', 'settings.placement': 'Entry Options', - 'placement.tab': 'Tab', 'placement.sidebar': 'Sidebar', 'placement.all': 'All', + 'placement.tab': 'Tab', + 'placement.sidebar': 'Sidebar', + 'placement.all': 'All', 'settings.insightsEntry': 'Context Insights entry', - 'insightsEntry.show': 'Show', 'insightsEntry.hide': 'Hide', + 'insightsEntry.show': 'Show', + 'insightsEntry.hide': 'Hide', 'settings.gran': 'Trend Chart · Default granularity', 'settings.mode': 'Trend Chart · Default display', 'settings.toolSort': 'Tool Schemas · Default sort', 'settings.fileSort': 'File Activity · Default sort', - 'settings.expand': 'Expand', 'settings.collapse': 'Collapse', + 'settings.expand': 'Expand', + 'settings.collapse': 'Collapse', 'settings.readOnly': 'Settings are read-only in this environment', - 'gran.total': 'Total', 'gran.delta': 'Delta', + 'gran.total': 'Total', + 'gran.delta': 'Delta', 'gran.modeHint': 'Total: cumulative makeup; Delta: change vs the previous request', 'trend.focus': 'Showing {cat} only', 'trend.adaptive': 'Adaptive', @@ -399,14 +509,23 @@ export const DICT_EN: Record = { 'events.at': 'Turn {t} · Step {s}', 'events.range': 'Turn {t} · Step {a}→{b}', 'events.rangeTo': 'Turn {a} · Step {as} → Turn {b} · Step {bs}', - 'kind.inject': 'Inject', 'kind.compaction': 'Compact', 'kind.prune': 'Prune', 'kind.model': 'Switch', 'kind.mode': 'Mode', + 'kind.inject': 'Inject', + 'kind.compaction': 'Compact', + 'kind.prune': 'Prune', + 'kind.model': 'Switch', + 'kind.mode': 'Mode', 'files.title': 'File Activity', 'files.scopeLatest': 'Up to latest · follows the trend chart pick', - 'files.kind.all': 'All', 'files.kind.read': 'Read', 'files.kind.write': 'Written', - 'files.kind.search': 'Searched', 'files.kind.image': 'Images', + 'files.kind.all': 'All', + 'files.kind.read': 'Read', + 'files.kind.write': 'Written', + 'files.kind.search': 'Searched', + 'files.kind.image': 'Images', 'files.chipTip': '{files} files · {ops} ops', 'files.search': 'Filter by path…', - 'files.sort.count': 'Most active', 'files.sort.latest': 'Latest', 'files.sort.path': 'By path', + 'files.sort.count': 'Most active', + 'files.sort.latest': 'Latest', + 'files.sort.path': 'By path', 'files.sortTip': 'Sort files by operation counts (of the selected kind when a kind chip is active), most recent activity, or path', 'files.files': '{n} files', 'files.deltaTip': 'Cumulative line change estimated from edit/write call arguments: + added / − removed', @@ -419,23 +538,53 @@ export const DICT_EN: Record = { 'files.preview': 'Preview in the sidebar', 'files.readEst': 'Lines read, estimated from the limit argument', 'files.errs': '{n} failed', - 'files.form.image': 'Image', 'files.form.dir': 'Directory', 'files.form.text': 'File', - 'files.glyph.root': 'Workspace root', 'files.glyph.tests': 'Tests', 'files.glyph.docs': 'Docs', - 'files.glyph.deps': 'Dependencies', 'files.glyph.build': 'Build output', 'files.glyph.scripts': 'Scripts', - 'files.glyph.config': 'Config', 'files.glyph.assets': 'Assets', 'files.glyph.hidden': 'Metadata directory', - 'files.glyph.lock': 'Lockfile', 'files.glyph.docker': 'Docker', 'files.glyph.ignore': 'Ignore rules', - 'files.glyph.license': 'License', 'files.glyph.python': 'Python', 'files.glyph.notebook': 'Notebook', - 'files.glyph.shell': 'Shell', 'files.glyph.style': 'Styles', - 'files.glyph.database': 'Database', 'files.glyph.data': 'Data / manifest', - 'files.glyph.markdown': 'Markup document', 'files.glyph.log': 'Log', 'files.glyph.sheet': 'Spreadsheet', - 'files.glyph.document': 'Document', 'files.glyph.archive': 'Archive', 'files.glyph.font': 'Font', + 'files.form.image': 'Image', + 'files.form.dir': 'Directory', + 'files.form.text': 'File', + 'files.glyph.root': 'Workspace root', + 'files.glyph.tests': 'Tests', + 'files.glyph.docs': 'Docs', + 'files.glyph.deps': 'Dependencies', + 'files.glyph.build': 'Build output', + 'files.glyph.scripts': 'Scripts', + 'files.glyph.config': 'Config', + 'files.glyph.assets': 'Assets', + 'files.glyph.hidden': 'Metadata directory', + 'files.glyph.lock': 'Lockfile', + 'files.glyph.docker': 'Docker', + 'files.glyph.ignore': 'Ignore rules', + 'files.glyph.license': 'License', + 'files.glyph.python': 'Python', + 'files.glyph.notebook': 'Notebook', + 'files.glyph.shell': 'Shell', + 'files.glyph.style': 'Styles', + 'files.glyph.database': 'Database', + 'files.glyph.data': 'Data / manifest', + 'files.glyph.markdown': 'Markup document', + 'files.glyph.log': 'Log', + 'files.glyph.sheet': 'Spreadsheet', + 'files.glyph.document': 'Document', + 'files.glyph.archive': 'Archive', + 'files.glyph.font': 'Font', 'files.glyph.media': 'Media', - 'files.glyph.lang.ts': 'TypeScript', 'files.glyph.lang.js': 'JavaScript', 'files.glyph.lang.go': 'Go', - 'files.glyph.lang.rust': 'Rust', 'files.glyph.lang.java': 'Java', 'files.glyph.lang.kotlin': 'Kotlin', - 'files.glyph.lang.ruby': 'Ruby', 'files.glyph.lang.php': 'PHP', 'files.glyph.lang.c': 'C', - 'files.glyph.lang.cpp': 'C++', 'files.glyph.lang.csharp': 'C#', 'files.glyph.lang.scala': 'Scala', - 'files.glyph.lang.lua': 'Lua', 'files.glyph.lang.dart': 'Dart', 'files.glyph.lang.swift': 'Swift', - 'files.glyph.lang.vue': 'Vue', 'files.glyph.lang.svelte': 'Svelte', 'files.glyph.lang.html': 'HTML', + 'files.glyph.lang.ts': 'TypeScript', + 'files.glyph.lang.js': 'JavaScript', + 'files.glyph.lang.go': 'Go', + 'files.glyph.lang.rust': 'Rust', + 'files.glyph.lang.java': 'Java', + 'files.glyph.lang.kotlin': 'Kotlin', + 'files.glyph.lang.ruby': 'Ruby', + 'files.glyph.lang.php': 'PHP', + 'files.glyph.lang.c': 'C', + 'files.glyph.lang.cpp': 'C++', + 'files.glyph.lang.csharp': 'C#', + 'files.glyph.lang.scala': 'Scala', + 'files.glyph.lang.lua': 'Lua', + 'files.glyph.lang.dart': 'Dart', + 'files.glyph.lang.swift': 'Swift', + 'files.glyph.lang.vue': 'Vue', + 'files.glyph.lang.svelte': 'Svelte', + 'files.glyph.lang.html': 'HTML', 'agents.title': 'Agent Network', 'agents.sub': 'Live context of this agent and its subagents · click a node to open its session', 'agents.chip.count': '{n} agents', @@ -473,9 +622,15 @@ export const DICT_EN: Record = { 'ev.prune': 'Tool output pruned', 'ev.skill': 'Skill injected ({name})', 'ev.model': 'Model switched: {a} → {b}', - 'ev.mode.plan.on': 'Plan mode on', 'ev.mode.plan.off': 'Plan mode off', - 'form.instructions': 'Instructions', 'form.catalog': 'Catalog Update', 'form.snapshot': 'State Snapshot', - 'form.notice': 'Notice', 'form.relay': 'Agent Relay', 'form.recall': 'Recall', 'form.context': 'Context Injection', + 'ev.mode.plan.on': 'Plan mode on', + 'ev.mode.plan.off': 'Plan mode off', + 'form.instructions': 'Instructions', + 'form.catalog': 'Catalog Update', + 'form.snapshot': 'State Snapshot', + 'form.notice': 'Notice', + 'form.relay': 'Agent Relay', + 'form.recall': 'Recall', + 'form.context': 'Context Injection', 'node.toolResult': 'Tool Result', 'node.calls': 'Calls ', 'node.empty': '(empty reply)', @@ -505,7 +660,9 @@ export const DICT_EN: Record = { 'browser.search.skill': 'Filter skills…', 'browser.search.assistant': 'Filter by reply or calls…', 'browser.search.tool': 'Filter by tool or arguments…', - 'browser.kind.think': 'Thinking', 'browser.kind.tool': 'Tools', 'browser.kind.answer': 'Answer', + 'browser.kind.think': 'Thinking', + 'browser.kind.tool': 'Tools', + 'browser.kind.answer': 'Answer', 'browser.kindTip': 'Filter replies by content kind: thinking, tool calls, or answer text', 'browser.rowNoMatch': 'No rows match the current filter', 'browser.loading': 'Loading full content from older session history…', @@ -520,7 +677,9 @@ export const DICT_EN: Record = { 'tool.jsonToggle': 'View Raw JSON', 'tool.jsonHide': 'Collapse', 'tool.search': 'Filter by name, description, or parameters…', - 'tool.sort.size': 'By size', 'tool.sort.count': 'By calls', 'tool.sort.name': 'By name', + 'tool.sort.size': 'By size', + 'tool.sort.count': 'By calls', + 'tool.sort.name': 'By name', 'tool.sortTip': 'Sort tools by call count in the shown step (default), token size, or name', 'tool.hitsTip': 'Times this tool was called and answered within the shown step’s context', 'tool.noMatch': 'No tools match the current filter', @@ -528,14 +687,21 @@ export const DICT_EN: Record = { 'rich.md': 'Markdown', 'rich.toMd': 'View as Markdown', 'rich.toRaw': 'View Raw Text', - 'rich.copy': 'Copy Raw Text', 'rich.copied': 'Copied', - 'rich.md.copy': 'Copy', 'rich.md.copied': 'Copied', + 'rich.copy': 'Copy Raw Text', + 'rich.copied': 'Copied', + 'rich.md.copy': 'Copy', + 'rich.md.copied': 'Copied', 'rich.md.footnotes': 'Footnotes', 'block.thinking': 'Reasoning', 'block.answer': 'Response', - 'block.content': 'Content', 'block.result': 'Result', 'block.summary': 'Summary', - 'block.line': '1 line', 'block.lines': '{n} lines', - 'call.ok': 'OK', 'call.fail': 'Failed', 'call.exit': 'exit {n}', + 'block.content': 'Content', + 'block.result': 'Result', + 'block.summary': 'Summary', + 'block.line': '1 line', + 'block.lines': '{n} lines', + 'call.ok': 'OK', + 'call.fail': 'Failed', + 'call.exit': 'exit {n}', 'node.failed': 'Tool execution failed', 'attach.images': 'Images', 'attach.other': 'Other content', @@ -553,7 +719,10 @@ export const DICT_EN: Record = { 'ov.title': 'Context Insights', 'ov.unavailable': 'The session list is unavailable', 'ov.range.label': 'Range', - 'ov.range.24h': 'Last 24 hours', 'ov.range.7d': 'Last 7 days', 'ov.range.30d': 'Last 30 days', 'ov.range.all': 'All', + 'ov.range.24h': 'Last 24 hours', + 'ov.range.7d': 'Last 7 days', + 'ov.range.30d': 'Last 30 days', + 'ov.range.all': 'All', 'ov.kpi.sessions': 'Active Sessions', 'ov.kpi.ofTotal': 'of {n} total', 'ov.kpi.tokens': 'Tokens Used', @@ -574,18 +743,33 @@ export const DICT_EN: Record = { 'ov.heat.sub': 'Filter by date', 'ov.heat.empty': 'No activity yet — daily usage appears once sessions make model requests', 'ov.heat.sessions': '{n} active sessions', - 'ov.heat.less': 'Less', 'ov.heat.more': 'More', - 'ov.heat.metric': 'Metric', 'ov.heat.metric.sessions': 'Sessions', 'ov.heat.metric.steps': 'Steps', + 'ov.heat.less': 'Less', + 'ov.heat.more': 'More', + 'ov.heat.metric': 'Metric', + 'ov.heat.metric.sessions': 'Sessions', + 'ov.heat.metric.steps': 'Steps', 'ov.heat.steps': '{n} steps', - 'ov.heat.wd.1': 'Mon', 'ov.heat.wd.3': 'Wed', 'ov.heat.wd.5': 'Fri', - 'ov.heat.mon.01': 'Jan', 'ov.heat.mon.02': 'Feb', 'ov.heat.mon.03': 'Mar', - 'ov.heat.mon.04': 'Apr', 'ov.heat.mon.05': 'May', 'ov.heat.mon.06': 'Jun', - 'ov.heat.mon.07': 'Jul', 'ov.heat.mon.08': 'Aug', 'ov.heat.mon.09': 'Sep', - 'ov.heat.mon.10': 'Oct', 'ov.heat.mon.11': 'Nov', 'ov.heat.mon.12': 'Dec', + 'ov.heat.wd.1': 'Mon', + 'ov.heat.wd.3': 'Wed', + 'ov.heat.wd.5': 'Fri', + 'ov.heat.mon.01': 'Jan', + 'ov.heat.mon.02': 'Feb', + 'ov.heat.mon.03': 'Mar', + 'ov.heat.mon.04': 'Apr', + 'ov.heat.mon.05': 'May', + 'ov.heat.mon.06': 'Jun', + 'ov.heat.mon.07': 'Jul', + 'ov.heat.mon.08': 'Aug', + 'ov.heat.mon.09': 'Sep', + 'ov.heat.mon.10': 'Oct', + 'ov.heat.mon.11': 'Nov', + 'ov.heat.mon.12': 'Dec', 'ov.list.title': 'Sessions', 'ov.list.search': 'Search title / path / last message…', 'ov.list.sortLabel': 'Sort', - 'ov.list.sort.recent': 'Last updated', 'ov.list.sort.tokens': 'Total Tokens', 'ov.list.sort.context': 'Current Context', + 'ov.list.sort.recent': 'Last updated', + 'ov.list.sort.tokens': 'Total Tokens', + 'ov.list.sort.context': 'Current Context', 'ov.list.empty': 'No sessions yet — each session’s context profile appears here once you start one', 'ov.list.noMatch': 'No sessions match the current filters', 'ov.list.noData': 'No context data yet — stats appear once the session makes model requests', diff --git a/src/client/index.ts b/src/client/index.ts index 537e533..0f92e0c 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -122,7 +122,10 @@ function apply(ctx: ClientCtx): void { const ContextModal = makeContextModal(ctx, kit, settings) ctx.slots.inject('conversation.input.overlay', () => { return ctx.slots.register( - { name: 'conversation.input.overlay', id: 'context-modal', order: 10, locale: NS, + { name: 'conversation.input.overlay', + id: 'context-modal', + order: 10, + locale: NS, inject: (sessionId = '') => ({ hooks: { contextModal: modalStoreOf(sessionId) } }) }, props => h(ContextModal, props), ) diff --git a/src/host/fold.ts b/src/host/fold.ts index 1142528..a18556d 100644 --- a/src/host/fold.ts +++ b/src/host/fold.ts @@ -1028,7 +1028,9 @@ export function applyTimeline(state: TimelineState, event: TimelineEvent, bounds const source = msg?.source if (isInjection(source)) { const rec: ContextEventRecord = { - seq: event.seq, time: event.time, kind: 'inject', + seq: event.seq, + time: event.time, + kind: 'inject', // Re-proved: the harness validates a source's `kind` but not its // `form`, so a hostile form must degrade to the default instead of // failing the record's strict wire/state schemas on every delivery @@ -1153,7 +1155,8 @@ export function applyTimeline(state: TimelineState, event: TimelineEvent, bounds bumpDetailRev(s) const total = s.systemTokens + s.toolsTokens + s.sums.user + s.sums.inject + s.sums.skill + s.sums.assistant + s.sums.tool const record: RequestRecord = { - time: event.time, seq: event.seq, + time: event.time, + seq: event.seq, system: s.systemTokens, tools: s.toolsTokens, user: s.sums.user, @@ -1294,7 +1297,9 @@ export function applyTimeline(state: TimelineState, event: TimelineEvent, bounds s.pendingShadowEventSeq = event.seq } s.events.push({ - seq: event.seq, time: event.time, kind: event.type === 'compaction/summary' ? 'compaction' : 'prune', + seq: event.seq, + time: event.time, + kind: event.type === 'compaction/summary' ? 'compaction' : 'prune', tokens: data && typeof data.shadowedTokenCount === 'number' ? data.shadowedTokenCount : 0, ...(event.type === 'compaction/summary' && data && Array.isArray(data.shadowedSeqs) ? { count: data.shadowedSeqs.length } diff --git a/src/shared/imageTokens.ts b/src/shared/imageTokens.ts index 4667afd..00b4e91 100644 --- a/src/shared/imageTokens.ts +++ b/src/shared/imageTokens.ts @@ -94,7 +94,10 @@ function safeResize(height: number, width: number, paddedHeight: number, paddedW const pad = COMPRESS_PAD_TO - 1 const budget = MAX_TOKENS - pad let result: ResizeSolution = { - nLlmH, nLlmW, bestHeight: paddedHeight, bestWidth: paddedWidth, + nLlmH, + nLlmW, + bestHeight: paddedHeight, + bestWidth: paddedWidth, numTokens: gridTokens(nLlmH, nLlmW), } if (result.numTokens > budget) {