@weibaohui/context-razor 0.4.1 → 0.4.2

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/client/bundle.js CHANGED
@@ -68,9 +68,9 @@ window.__ModuleLoader__.load({
68
68
  orderNote: '本插件不会改变上下文条目顺序,只会剔除选中条目。',
69
69
  legendTitle: '按 token 量级筛选(点选高亮某一档)',
70
70
  kindFilterLabel: '类型',
71
- selectVisible: '选中可见',
72
- selectVisibleHint: '选中当前显示的全部条目(配合色块筛选)',
73
- clearSelect: '清空选择',
71
+ selectVisible: '全选',
72
+ selectVisibleHint: '全选当前可见条目(配合类型/量级筛选)',
73
+ clearSelect: '取消全选',
74
74
  deleteSelected: '删除选中',
75
75
  selectedStats: '已选 {n} 条 / ≈{tokens} token',
76
76
  stats: '{nodes} 条 · 合计 ≈{tokens} token{mode}',
@@ -110,9 +110,9 @@ window.__ModuleLoader__.load({
110
110
  orderNote: 'This plugin never reorders context entries; it only removes the selected ones.',
111
111
  legendTitle: 'Filter by token tier (click to isolate a band)',
112
112
  kindFilterLabel: 'Type',
113
- selectVisible: 'Select shown',
114
- selectVisibleHint: 'Select every currently shown entry (pairs with tier filters)',
115
- clearSelect: 'Clear selection',
113
+ selectVisible: 'All',
114
+ selectVisibleHint: 'Select all currently visible entries (pairs with type/tier filters)',
115
+ clearSelect: 'Clear all',
116
116
  deleteSelected: 'Delete selected',
117
117
  selectedStats: '{n} selected / ≈{tokens} tokens',
118
118
  stats: '{nodes} entries · total ≈{tokens} tokens{mode}',
@@ -271,6 +271,11 @@ window.__ModuleLoader__.load({
271
271
  .rz-kindbtn:hover{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-interactive-bg-hover)}
272
272
  .rz-kindbtn.on{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);font-weight:600}
273
273
  .rz-kindcount{font-size:10px;opacity:.7}
274
+ .rz-thead{display:flex;gap:8px;align-items:center;flex-wrap:wrap;padding:6px 12px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1)}
275
+ .rz-thead-l{display:inline-flex;gap:8px;align-items:center;flex-wrap:wrap}
276
+ .rz-sel-stats{color:var(--dsw-alias-label-secondary);font-size:12px;white-space:nowrap}
277
+ .rz-col-tok{flex:0 0 62px;display:flex;justify-content:flex-end}
278
+ .rz-col-pct{flex:0 0 44px;text-align:right;color:var(--dsw-alias-label-tertiary);font-size:11px;white-space:nowrap}
274
279
  .rz-list{display:flex;flex-direction:column;gap:6px}
275
280
  .rz-row{display:flex;gap:10px;align-items:center;padding:8px 12px;border-radius:10px;border:1px solid var(--dsw-alias-border-l1);border-left:3px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);cursor:pointer;text-align:left;width:100%}
276
281
  .rz-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
@@ -313,12 +318,10 @@ window.__ModuleLoader__.load({
313
318
 
314
319
  const Chip = ({ kind, label, title }) => h('span', { className: 'rz-chip ' + kind, title }, label)
315
320
 
316
- const TokenBadge = ({ entry, total }) => {
321
+ const TokenBadge = ({ entry }) => {
317
322
  const tier = tierOf(entry)
318
- const pct = pctOf(entry.tokens, total)
319
- const title = RAZOR_TIERS[tier].label + ' token' + (pct ? ' · ' + pct + ' ' + (total ? 'of ≈' + formatNum(total) + ' token' : 'of context') : '')
320
- return h('span', { className: 'rz-badge tier-' + tier, title },
321
- '≈' + formatNum(entry.tokens) + (pct ? ' · ' + pct : ''))
323
+ return h('span', { className: 'rz-badge tier-' + tier, title: RAZOR_TIERS[tier].label + ' token' },
324
+ '≈' + formatNum(entry.tokens))
322
325
  }
323
326
 
324
327
  /** 分页列表:先渲染 pageSize 行,按需增长(大会话一次挂几千行会卡)。 */
@@ -335,12 +338,14 @@ window.__ModuleLoader__.load({
335
338
  /** 单条全文弹窗(正文经 /entry 异步补全)。 */
336
339
  function DetailModal({ detail, t, total, onClose }) {
337
340
  if (!detail) return null
341
+ const pct = pctOf(detail.tokens, total)
338
342
  return h('div', { className: 'rz-dlg-backdrop', onClick: onClose },
339
343
  h('div', { className: 'rz-dlg', onClick: e => e.stopPropagation() },
340
344
  h('h3', null, t('detailTitle') + ' · ' + t('seqLabel', { seq: detail.seq })),
341
345
  h('div', { className: 'rz-stats', style: { marginBottom: 10 } },
342
346
  h(Chip, { ...entryChip(detail, t) }),
343
- h(TokenBadge, { entry: detail, total }),
347
+ h(TokenBadge, { entry: detail }),
348
+ pct && h('span', { className: 'rz-label', title: pct + ' of ≈' + formatNum(total) + ' token' }, pct),
344
349
  h('span', { className: 'rz-label', title: 'seq ' + detail.seq }, formatDateTime(detail.time)),
345
350
  h('span', { className: 'rz-label' }, t('detailChars', { chars: formatNum(detail.chars) })),
346
351
  detail.usage && h('span', { className: 'rz-label' }, t('detailUsage', {
@@ -493,13 +498,7 @@ window.__ModuleLoader__.load({
493
498
  h('span', { className: 'rz-legend' + (tierFilter.size > 0 ? ' filtering' : ''), title: t('legendTitle') },
494
499
  RAZOR_TIERS.map((tr, i) => h('button', { key: i, className: 'rz-swatch tier-' + i + (tierFilter.has(i) ? ' on' : ''), title: tr.label + ' token', onClick: () => toggleTier(i) }, tr.label))),
495
500
  h('span', { className: 'rz-spacer' }),
496
- busy && h('span', { className: 'rz-badge' }, t('busyTag')),
497
- h('button', { className: 'rz-btn', onClick: selectVisible, disabled: visible.length === 0 || busy, title: t('selectVisibleHint') },
498
- t('selectVisible') + (visible.length ? ` (${visible.length})` : '')),
499
- h('select', { className: 'rz-select', value: sortBy, onChange: e => setSortBy(e.target.value), title: t('sortLabel'), 'aria-label': t('sortLabel') },
500
- h('option', { value: 'order' }, t('sortOrder')),
501
- h('option', { value: 'tokens' }, t('sortTokens'))),
502
- h('button', { className: 'rz-btn', onClick: refreshAll, title: t('refresh') }, t('refresh'))),
501
+ busy && h('span', { className: 'rz-badge' }, t('busyTag'))),
503
502
  // 类型筛选:与 chip 同口径的可点选按钮(user/injected/assistant/tool:<名>),
504
503
  // 多选 toggle,与 tier 量级筛选正交叠加。按钮带条数;hover 看 token 占比。
505
504
  categories.length > 0 && h('div', { key: 'kinds', className: 'rz-kindsbar' },
@@ -513,25 +512,33 @@ window.__ModuleLoader__.load({
513
512
  onClick: () => toggleKind(cat) },
514
513
  categoryLabel(cat, t),
515
514
  h('span', { className: 'rz-kindcount' }, count))))),
516
- selected.size > 0 && h('div', { key: 'sel', className: 'rz-stats' },
517
- h('span', null, t('selectedStats', { n: selected.size, tokens: formatNum(selectedTokens) })),
518
- h('span', { className: 'rz-spacer' }),
519
- h('div', { className: 'rz-footbtns' },
520
- h('button', { className: 'rz-btn', onClick: () => setSelected(new Set()) }, t('clearSelect')),
521
- h('button', { className: 'rz-btn rz-btn-danger', disabled: !canDelete,
515
+ // 表头(在类型筛选下方):左 刷新/全选,选中后追加 删除·取消全选;右 排序。
516
+ h('div', { key: 'thead', className: 'rz-thead' },
517
+ h('div', { className: 'rz-thead-l' },
518
+ h('button', { className: 'rz-btn', onClick: refreshAll, title: t('refresh') }, t('refresh')),
519
+ h('button', { className: 'rz-btn', onClick: selectVisible, disabled: visible.length === 0 || busy, title: t('selectVisibleHint') },
520
+ t('selectVisible') + (visible.length ? ` (${visible.length})` : '')),
521
+ selected.size > 0 && h('button', { className: 'rz-btn rz-btn-danger', disabled: !canDelete,
522
522
  title: busy ? t('deleteBusyHint') : undefined,
523
523
  onClick: () => setConfirming([...selected]) },
524
- t('deleteSelected') + ` (${selected.size})`))),
524
+ t('deleteSelected') + ` (${selected.size})`),
525
+ selected.size > 0 && h('button', { className: 'rz-btn', onClick: () => setSelected(new Set()) }, t('clearSelect'))),
526
+ h('span', { className: 'rz-spacer' }),
527
+ h('select', { className: 'rz-select', value: sortBy, onChange: e => setSortBy(e.target.value), title: t('sortLabel'), 'aria-label': t('sortLabel') },
528
+ h('option', { value: 'order' }, t('sortOrder')),
529
+ h('option', { value: 'tokens' }, t('sortTokens')))),
525
530
  visible.length === 0
526
531
  ? h('div', { key: 'empty', className: 'rz-empty' }, t('emptyContext'))
527
532
  : h('div', { key: 'list', className: 'rz-list' },
528
533
  h(PagedList, { items: visible, t, render: entry => {
529
534
  const tier = tierOf(entry)
535
+ const pct = pctOf(entry.tokens, context.totalTokens)
530
536
  return h('div', { key: entry.seq, className: 'rz-row tier-' + tier + (selected.has(entry.seq) ? ' checked' : ''),
531
537
  role: 'button', tabIndex: 0, onClick: () => toggleRow(entry.seq),
532
538
  onKeyDown: e => e.key === 'Enter' && toggleRow(entry.seq) },
533
539
  h('input', { type: 'checkbox', checked: selected.has(entry.seq), onClick: e => e.stopPropagation(), onChange: () => toggleRow(entry.seq) }),
534
- h(TokenBadge, { entry, total: context.totalTokens }),
540
+ h('span', { className: 'rz-col-tok' }, h(TokenBadge, { entry })),
541
+ h('span', { className: 'rz-col-pct', title: pct ? pct + ' of ≈' + formatNum(context.totalTokens) + ' token' : '' }, pct || ''),
535
542
  h(Chip, { ...entryChip(entry, t) }),
536
543
  h('span', { className: 'rz-row-preview', title: entry.preview,
537
544
  onClick: e => { e.stopPropagation(); openDetail(entry) } }, entry.preview || ' '),
package/client/index.js CHANGED
@@ -58,9 +58,9 @@ const ZH = {
58
58
  orderNote: '本插件不会改变上下文条目顺序,只会剔除选中条目。',
59
59
  legendTitle: '按 token 量级筛选(点选高亮某一档)',
60
60
  kindFilterLabel: '类型',
61
- selectVisible: '选中可见',
62
- selectVisibleHint: '选中当前显示的全部条目(配合色块筛选)',
63
- clearSelect: '清空选择',
61
+ selectVisible: '全选',
62
+ selectVisibleHint: '全选当前可见条目(配合类型/量级筛选)',
63
+ clearSelect: '取消全选',
64
64
  deleteSelected: '删除选中',
65
65
  selectedStats: '已选 {n} 条 / ≈{tokens} token',
66
66
  stats: '{nodes} 条 · 合计 ≈{tokens} token{mode}',
@@ -100,9 +100,9 @@ const EN = {
100
100
  orderNote: 'This plugin never reorders context entries; it only removes the selected ones.',
101
101
  legendTitle: 'Filter by token tier (click to isolate a band)',
102
102
  kindFilterLabel: 'Type',
103
- selectVisible: 'Select shown',
104
- selectVisibleHint: 'Select every currently shown entry (pairs with tier filters)',
105
- clearSelect: 'Clear selection',
103
+ selectVisible: 'All',
104
+ selectVisibleHint: 'Select all currently visible entries (pairs with type/tier filters)',
105
+ clearSelect: 'Clear all',
106
106
  deleteSelected: 'Delete selected',
107
107
  selectedStats: '{n} selected / ≈{tokens} tokens',
108
108
  stats: '{nodes} entries · total ≈{tokens} tokens{mode}',
@@ -261,6 +261,11 @@ const STYLE = `<style>
261
261
  .rz-kindbtn:hover{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-interactive-bg-hover)}
262
262
  .rz-kindbtn.on{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);font-weight:600}
263
263
  .rz-kindcount{font-size:10px;opacity:.7}
264
+ .rz-thead{display:flex;gap:8px;align-items:center;flex-wrap:wrap;padding:6px 12px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1)}
265
+ .rz-thead-l{display:inline-flex;gap:8px;align-items:center;flex-wrap:wrap}
266
+ .rz-sel-stats{color:var(--dsw-alias-label-secondary);font-size:12px;white-space:nowrap}
267
+ .rz-col-tok{flex:0 0 62px;display:flex;justify-content:flex-end}
268
+ .rz-col-pct{flex:0 0 44px;text-align:right;color:var(--dsw-alias-label-tertiary);font-size:11px;white-space:nowrap}
264
269
  .rz-list{display:flex;flex-direction:column;gap:6px}
265
270
  .rz-row{display:flex;gap:10px;align-items:center;padding:8px 12px;border-radius:10px;border:1px solid var(--dsw-alias-border-l1);border-left:3px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);cursor:pointer;text-align:left;width:100%}
266
271
  .rz-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
@@ -303,12 +308,10 @@ const STYLE = `<style>
303
308
 
304
309
  const Chip = ({ kind, label, title }) => h('span', { className: 'rz-chip ' + kind, title }, label)
305
310
 
306
- const TokenBadge = ({ entry, total }) => {
311
+ const TokenBadge = ({ entry }) => {
307
312
  const tier = tierOf(entry)
308
- const pct = pctOf(entry.tokens, total)
309
- const title = RAZOR_TIERS[tier].label + ' token' + (pct ? ' · ' + pct + ' ' + (total ? 'of ≈' + formatNum(total) + ' token' : 'of context') : '')
310
- return h('span', { className: 'rz-badge tier-' + tier, title },
311
- '≈' + formatNum(entry.tokens) + (pct ? ' · ' + pct : ''))
313
+ return h('span', { className: 'rz-badge tier-' + tier, title: RAZOR_TIERS[tier].label + ' token' },
314
+ '≈' + formatNum(entry.tokens))
312
315
  }
313
316
 
314
317
  /** 分页列表:先渲染 pageSize 行,按需增长(大会话一次挂几千行会卡)。 */
@@ -325,12 +328,14 @@ function PagedList({ items, render, t }) {
325
328
  /** 单条全文弹窗(正文经 /entry 异步补全)。 */
326
329
  function DetailModal({ detail, t, total, onClose }) {
327
330
  if (!detail) return null
331
+ const pct = pctOf(detail.tokens, total)
328
332
  return h('div', { className: 'rz-dlg-backdrop', onClick: onClose },
329
333
  h('div', { className: 'rz-dlg', onClick: e => e.stopPropagation() },
330
334
  h('h3', null, t('detailTitle') + ' · ' + t('seqLabel', { seq: detail.seq })),
331
335
  h('div', { className: 'rz-stats', style: { marginBottom: 10 } },
332
336
  h(Chip, { ...entryChip(detail, t) }),
333
- h(TokenBadge, { entry: detail, total }),
337
+ h(TokenBadge, { entry: detail }),
338
+ pct && h('span', { className: 'rz-label', title: pct + ' of ≈' + formatNum(total) + ' token' }, pct),
334
339
  h('span', { className: 'rz-label', title: 'seq ' + detail.seq }, formatDateTime(detail.time)),
335
340
  h('span', { className: 'rz-label' }, t('detailChars', { chars: formatNum(detail.chars) })),
336
341
  detail.usage && h('span', { className: 'rz-label' }, t('detailUsage', {
@@ -483,13 +488,7 @@ function RazorPage({ t, fixedSessionId }) {
483
488
  h('span', { className: 'rz-legend' + (tierFilter.size > 0 ? ' filtering' : ''), title: t('legendTitle') },
484
489
  RAZOR_TIERS.map((tr, i) => h('button', { key: i, className: 'rz-swatch tier-' + i + (tierFilter.has(i) ? ' on' : ''), title: tr.label + ' token', onClick: () => toggleTier(i) }, tr.label))),
485
490
  h('span', { className: 'rz-spacer' }),
486
- busy && h('span', { className: 'rz-badge' }, t('busyTag')),
487
- h('button', { className: 'rz-btn', onClick: selectVisible, disabled: visible.length === 0 || busy, title: t('selectVisibleHint') },
488
- t('selectVisible') + (visible.length ? ` (${visible.length})` : '')),
489
- h('select', { className: 'rz-select', value: sortBy, onChange: e => setSortBy(e.target.value), title: t('sortLabel'), 'aria-label': t('sortLabel') },
490
- h('option', { value: 'order' }, t('sortOrder')),
491
- h('option', { value: 'tokens' }, t('sortTokens'))),
492
- h('button', { className: 'rz-btn', onClick: refreshAll, title: t('refresh') }, t('refresh'))),
491
+ busy && h('span', { className: 'rz-badge' }, t('busyTag'))),
493
492
  // 类型筛选:与 chip 同口径的可点选按钮(user/injected/assistant/tool:<名>),
494
493
  // 多选 toggle,与 tier 量级筛选正交叠加。按钮带条数;hover 看 token 占比。
495
494
  categories.length > 0 && h('div', { key: 'kinds', className: 'rz-kindsbar' },
@@ -503,25 +502,33 @@ function RazorPage({ t, fixedSessionId }) {
503
502
  onClick: () => toggleKind(cat) },
504
503
  categoryLabel(cat, t),
505
504
  h('span', { className: 'rz-kindcount' }, count))))),
506
- selected.size > 0 && h('div', { key: 'sel', className: 'rz-stats' },
507
- h('span', null, t('selectedStats', { n: selected.size, tokens: formatNum(selectedTokens) })),
508
- h('span', { className: 'rz-spacer' }),
509
- h('div', { className: 'rz-footbtns' },
510
- h('button', { className: 'rz-btn', onClick: () => setSelected(new Set()) }, t('clearSelect')),
511
- h('button', { className: 'rz-btn rz-btn-danger', disabled: !canDelete,
505
+ // 表头(在类型筛选下方):左 刷新/全选,选中后追加 删除·取消全选;右 排序。
506
+ h('div', { key: 'thead', className: 'rz-thead' },
507
+ h('div', { className: 'rz-thead-l' },
508
+ h('button', { className: 'rz-btn', onClick: refreshAll, title: t('refresh') }, t('refresh')),
509
+ h('button', { className: 'rz-btn', onClick: selectVisible, disabled: visible.length === 0 || busy, title: t('selectVisibleHint') },
510
+ t('selectVisible') + (visible.length ? ` (${visible.length})` : '')),
511
+ selected.size > 0 && h('button', { className: 'rz-btn rz-btn-danger', disabled: !canDelete,
512
512
  title: busy ? t('deleteBusyHint') : undefined,
513
513
  onClick: () => setConfirming([...selected]) },
514
- t('deleteSelected') + ` (${selected.size})`))),
514
+ t('deleteSelected') + ` (${selected.size})`),
515
+ selected.size > 0 && h('button', { className: 'rz-btn', onClick: () => setSelected(new Set()) }, t('clearSelect'))),
516
+ h('span', { className: 'rz-spacer' }),
517
+ h('select', { className: 'rz-select', value: sortBy, onChange: e => setSortBy(e.target.value), title: t('sortLabel'), 'aria-label': t('sortLabel') },
518
+ h('option', { value: 'order' }, t('sortOrder')),
519
+ h('option', { value: 'tokens' }, t('sortTokens')))),
515
520
  visible.length === 0
516
521
  ? h('div', { key: 'empty', className: 'rz-empty' }, t('emptyContext'))
517
522
  : h('div', { key: 'list', className: 'rz-list' },
518
523
  h(PagedList, { items: visible, t, render: entry => {
519
524
  const tier = tierOf(entry)
525
+ const pct = pctOf(entry.tokens, context.totalTokens)
520
526
  return h('div', { key: entry.seq, className: 'rz-row tier-' + tier + (selected.has(entry.seq) ? ' checked' : ''),
521
527
  role: 'button', tabIndex: 0, onClick: () => toggleRow(entry.seq),
522
528
  onKeyDown: e => e.key === 'Enter' && toggleRow(entry.seq) },
523
529
  h('input', { type: 'checkbox', checked: selected.has(entry.seq), onClick: e => e.stopPropagation(), onChange: () => toggleRow(entry.seq) }),
524
- h(TokenBadge, { entry, total: context.totalTokens }),
530
+ h('span', { className: 'rz-col-tok' }, h(TokenBadge, { entry })),
531
+ h('span', { className: 'rz-col-pct', title: pct ? pct + ' of ≈' + formatNum(context.totalTokens) + ' token' : '' }, pct || ''),
525
532
  h(Chip, { ...entryChip(entry, t) }),
526
533
  h('span', { className: 'rz-row-preview', title: entry.preview,
527
534
  onClick: e => { e.stopPropagation(); openDetail(entry) } }, entry.preview || ' '),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weibaohui/context-razor",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "dsh 插件 · 上下文剃刀:把当前会话上下文逐条列出(角色/预览/≈token,cl100k 估算),超阈值标红,勾选后精确裁剪——不经 LLM 总结,删了什么一目了然。",
5
5
  "license": "MIT",
6
6
  "keywords": [