@musnows/scriverse 1.0.6 → 1.0.8

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.
@@ -1,3 +1,25 @@
1
+ export function formatAiContextCacheHitPercent(value) {
2
+ const rate = Number(value);
3
+ if (!Number.isFinite(rate)) return "—";
4
+ const clamped = Math.max(0, Math.min(100, Math.round((rate + Number.EPSILON) * 10) / 10));
5
+ const label = Number.isInteger(clamped) ? String(clamped) : clamped.toFixed(1);
6
+ return `${label}%`;
7
+ }
8
+
9
+ export function formatAiContextPercentSummary(usage) {
10
+ const distribution = normalizeAiContextTokenDistribution(usage);
11
+ const contextPercent = formatAiContextUsagePercent(distribution.occupiedTokens, distribution.contextWindow);
12
+ return `context ${contextPercent} · cache hit ${formatAiContextCacheHitPercent(usage?.cacheHitPercent)}`;
13
+ }
14
+
15
+ export function formatAiContextPopoverDescription(usage) {
16
+ if (!usage) return "选择可用模型后显示当前上下文用量";
17
+ const distribution = normalizeAiContextTokenDistribution(usage);
18
+ const occupied = distribution.occupiedTokens.toLocaleString("zh-CN");
19
+ const contextWindow = distribution.contextWindow.toLocaleString("zh-CN");
20
+ return `已占用 ${occupied} / ${contextWindow} tok · ${formatAiContextPercentSummary(usage)}`;
21
+ }
22
+
1
23
  export function formatAiContextUsageTooltip(usage) {
2
24
  if (!usage) return "选择可用模型后显示当前上下文用量";
3
25
  const inputTokens = Math.max(0, Math.round(Number(usage.inputTokens) || 0)).toLocaleString("zh-CN");
@@ -6,7 +28,23 @@ export function formatAiContextUsageTooltip(usage) {
6
28
  const conversationTokens = Math.max(0, Math.round(Number(usage.conversationTokens) || 0)).toLocaleString("zh-CN");
7
29
  const conversationBudget = Math.max(0, Math.round(Number(usage.conversationBudgetTokens) || 0)).toLocaleString("zh-CN");
8
30
  const outputTokens = Math.max(0, Math.round(Number(usage.outputTokens) || 0)).toLocaleString("zh-CN");
9
- return `总输入 ${inputTokens} / ${contextWindow} tok · 作品上下文 ${contextTokens} tok · 对话历史 ${conversationTokens} / ${conversationBudget} tok · 当前调用实际输出 ${outputTokens} tok`;
31
+ return `总输入 ${inputTokens} / ${contextWindow} tok · 作品上下文 ${contextTokens} tok · 对话历史 ${conversationTokens} / ${conversationBudget} tok · 当前调用实际输出 ${outputTokens} tok · ${formatAiContextPercentSummary(usage)}`;
32
+ }
33
+
34
+ export function attachAiContextCacheHitPercent(usage, cacheHitPercent) {
35
+ if (!usage || typeof usage !== "object" || Array.isArray(usage)) return usage ?? null;
36
+ const rate = Number(cacheHitPercent);
37
+ if (!Number.isFinite(rate)) return usage;
38
+ return { ...usage, cacheHitPercent: Math.max(0, Math.min(100, rate)) };
39
+ }
40
+
41
+ function withPreservedCacheHitPercent(previousUsage, nextUsage) {
42
+ if (!nextUsage || typeof nextUsage !== "object" || Array.isArray(nextUsage)) return nextUsage;
43
+ const nextCacheHit = Number(nextUsage.cacheHitPercent);
44
+ if (Number.isFinite(nextCacheHit)) return nextUsage;
45
+ const previousCacheHit = Number(previousUsage?.cacheHitPercent);
46
+ if (!Number.isFinite(previousCacheHit)) return nextUsage;
47
+ return { ...nextUsage, cacheHitPercent: previousCacheHit };
10
48
  }
11
49
 
12
50
  function tokenCount(value) {
@@ -14,7 +52,8 @@ function tokenCount(value) {
14
52
  }
15
53
 
16
54
  export function resolveAiContextUsage(previousUsage, nextUsage) {
17
- return nextUsage && typeof nextUsage === "object" ? nextUsage : previousUsage ?? null;
55
+ if (!nextUsage || typeof nextUsage !== "object") return previousUsage ?? null;
56
+ return withPreservedCacheHitPercent(previousUsage, nextUsage);
18
57
  }
19
58
 
20
59
  const maximumAiContextUsageFields = [
@@ -28,7 +67,9 @@ const minimumAiContextUsageFields = ["remainingTokens"];
28
67
 
29
68
  export function mergeAiContextUsage(previousUsage, nextUsage, allowShrink = false) {
30
69
  if (!nextUsage || typeof nextUsage !== "object" || Array.isArray(nextUsage)) return previousUsage ?? null;
31
- if (!previousUsage || typeof previousUsage !== "object" || Array.isArray(previousUsage) || allowShrink) return nextUsage;
70
+ if (!previousUsage || typeof previousUsage !== "object" || Array.isArray(previousUsage) || allowShrink) {
71
+ return withPreservedCacheHitPercent(previousUsage, nextUsage);
72
+ }
32
73
  const mergedUsage = { ...nextUsage };
33
74
  for (const field of [...maximumAiContextUsageFields, ...minimumAiContextUsageFields]) {
34
75
  const previousValue = Number(previousUsage[field]);
@@ -41,7 +82,7 @@ export function mergeAiContextUsage(previousUsage, nextUsage, allowShrink = fals
41
82
  else if (Number.isFinite(previousValue)) mergedUsage[field] = previousValue;
42
83
  else if (Number.isFinite(nextValue)) mergedUsage[field] = nextValue;
43
84
  }
44
- return mergedUsage;
85
+ return withPreservedCacheHitPercent(previousUsage, mergedUsage);
45
86
  }
46
87
 
47
88
  export function formatAiContextUsagePercent(occupiedTokens, contextWindow) {
@@ -0,0 +1,30 @@
1
+ export function createAiRenderScheduler({ isVisible, isConnected = () => true, schedule = requestAnimationFrame, now = () => performance.now(), intervalMs = 32, budgetMs = 4 }) {
2
+ const pending = new Map();
3
+ let frame = null;
4
+ let lastRendered = -Infinity;
5
+ const refresh = () => {
6
+ for (const target of pending.keys()) {
7
+ if (!isConnected(target)) pending.delete(target);
8
+ }
9
+ if (frame !== null || ![...pending.keys()].some(isVisible)) return;
10
+ frame = schedule(() => {
11
+ frame = null;
12
+ const started = now();
13
+ if (started - lastRendered >= intervalMs) {
14
+ for (const [target, render] of pending) {
15
+ if (!isConnected(target)) { pending.delete(target); continue; }
16
+ if (!isVisible(target)) continue;
17
+ pending.delete(target);
18
+ render();
19
+ lastRendered = started;
20
+ if (now() - started >= budgetMs) break;
21
+ }
22
+ }
23
+ refresh();
24
+ });
25
+ };
26
+ return {
27
+ enqueue(target, render) { pending.set(target, render); refresh(); },
28
+ refresh
29
+ };
30
+ }