opencode-mask-j0k3r-dev-rgl 2.0.19 → 2.0.21

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.
Files changed (3) hide show
  1. package/components.tsx +9 -25
  2. package/package.json +1 -1
  3. package/tui.tsx +68 -13
package/components.tsx CHANGED
@@ -1,7 +1,6 @@
1
1
  // @ts-nocheck
2
2
  /** @jsxImportSource @opentui/solid */
3
- import type { TuiThemeCurrent, TuiSidebarFileItem, TuiSidebarMcpItem, TuiSidebarLspItem, TuiSidebarTodoItem } from "@opencode-ai/plugin/tui"
4
- import type { Message } from "@opencode-ai/sdk/v2"
3
+ import type { TuiThemeCurrent, TuiSidebarMcpItem, TuiSidebarLspItem, TuiSidebarTodoItem } from "@opencode-ai/plugin/tui"
5
4
  import type { Cfg } from "./config"
6
5
  import { getOSName, getProviders } from "./detection"
7
6
  import {
@@ -77,24 +76,18 @@ const ProgressBar = (props: {
77
76
  export const SidebarArch = (props: {
78
77
  theme: TuiThemeCurrent
79
78
  config: Cfg
80
- sessionID?: string
81
79
  branch?: string
82
- files?: ReadonlyArray<TuiSidebarFileItem>
83
80
  mcpItems?: ReadonlyArray<TuiSidebarMcpItem>
84
81
  lspItems?: ReadonlyArray<TuiSidebarLspItem>
85
82
  todos?: ReadonlyArray<TuiSidebarTodoItem>
86
- messages?: ReadonlyArray<Message>
83
+ contextTokens: number
84
+ contextCost: number
85
+ contextLimit: number
87
86
  }) => {
88
87
  if (!props.config.show_sidebar) return null
89
88
 
90
89
  const t = props.theme
91
90
 
92
- // ── Files ─────────────────────────────────────────────────────────────────
93
- const files = props.files ?? []
94
- const totalAdditions = files.reduce((s, f) => s + f.additions, 0)
95
- const totalDeletions = files.reduce((s, f) => s + f.deletions, 0)
96
- const totalChanges = totalAdditions + totalDeletions
97
-
98
91
  // ── Todos ─────────────────────────────────────────────────────────────────
99
92
  const todos = props.todos ?? []
100
93
  const doneTodos = todos.filter(t => t.status === "completed").length
@@ -112,20 +105,11 @@ export const SidebarArch = (props: {
112
105
  const lspActive = lspItems.filter(l => l.status === "idle" || l.status === "running").length
113
106
  const lspTotal = lspItems.length
114
107
 
115
- // ── Tokens & Cost ─────────────────────────────────────────────────────────
116
- // El contexto = input máximo entre todos los mensajes asistente
117
- // (el input crece conforme el contexto se acumula; el mayor = el más reciente sustancial)
118
- const messages = props.messages ?? []
119
- const assistantMsgs = messages.filter(m => m.role === "assistant")
120
- const contextTokens = assistantMsgs.reduce((max, m) => Math.max(max, m.tokens?.input ?? 0), 0)
121
- const totalCost = assistantMsgs.reduce((s, m) => s + (m.cost ?? 0), 0)
122
-
123
- // % used: OpenCode lo calcula internamente con el context window del modelo.
124
- // Inferimos el límite desde los datos disponibles: si tenemos tokens y %,
125
- // podemos intentar inferirlo. Sin esa info, usamos 1M (Claude 3.5/Gemini 1.5).
126
- const CONTEXT_LIMIT = 1_000_000
127
- const contextPct = Math.min(100, Math.round((contextTokens / CONTEXT_LIMIT) * 100))
128
- // El % de costo: escalamos $1.00 = 100%
108
+ // ── Context valores ya calculados en tui.tsx via eventos ────────────────
109
+ const contextTokens = props.contextTokens
110
+ const totalCost = props.contextCost
111
+ const contextLimit = props.contextLimit
112
+ const contextPct = Math.min(100, Math.round((contextTokens / contextLimit) * 100))
129
113
  const costPct = Math.min(100, Math.round(totalCost * 100))
130
114
 
131
115
  const fmtTokens = (n: number) => n >= 1000 ? `${(n / 1000).toFixed(1)}k` : `${n}`
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "opencode-mask-j0k3r-dev-rgl",
4
- "version": "2.0.19",
4
+ "version": "2.0.21",
5
5
  "description": "Arch Linux TUI mask for OpenCode — hot pink theme with prominent ASCII logo and j0k3r-dev-rgl@latest legend",
6
6
  "type": "module",
7
7
  "exports": {
package/tui.tsx CHANGED
@@ -1,6 +1,7 @@
1
1
  // @ts-nocheck
2
2
  /** @jsxImportSource @opentui/solid */
3
3
  import type { TuiPlugin, TuiPluginModule } from "@opencode-ai/plugin/tui"
4
+ import { createSignal } from "solid-js"
4
5
  import { cfg } from "./config"
5
6
  import { HomeLogo, SidebarArch, DetectedEnv } from "./components"
6
7
 
@@ -15,36 +16,91 @@ const tui: TuiPlugin = async (api, options) => {
15
16
  const boot = cfg(rec(options))
16
17
  if (!boot.enabled) return
17
18
 
18
- // Theme setup (wrapped in try-catch)
19
+ // Theme setup
19
20
  try {
20
21
  await api.theme.install("./themes/j0k3r-dev-rgl.json")
21
- if (boot.set_theme) {
22
- api.theme.set(boot.theme)
23
- }
22
+ if (boot.set_theme) api.theme.set(boot.theme)
24
23
  } catch (error) {
25
24
  console.error("[j0k3r-dev-rgl] Theme setup failed:", error)
26
25
  }
27
26
 
27
+ // ── Reactive context tracker via events ──────────────────────────────────
28
+ // sessionID → { tokens, cost }
29
+ type CtxStats = { tokens: number; cost: number }
30
+ const ctxStore = new Map<string, CtxStats>()
31
+ const msgCostTracker = new Map<string, number>() // `sid:msgId` → cost
32
+
33
+ const [ctxTick, setCtxTick] = createSignal(0)
34
+
35
+ api.event.on("message.updated", (event) => {
36
+ const msg = event.properties as any
37
+ if (!msg || msg.role !== "assistant") return
38
+
39
+ // Buscar sessionID en todos los campos posibles del mensaje
40
+ const sid = msg.sessionID ?? msg.session_id ?? msg.parentID ?? null
41
+ if (!sid) return
42
+
43
+ // Tokens: intentamos todos los campos posibles y tomamos el mayor
44
+ const t = msg.tokens ?? {}
45
+ const tokens = Math.max(
46
+ t.input ?? 0,
47
+ t.total ?? 0,
48
+ (t.input ?? 0) + (t.output ?? 0),
49
+ )
50
+
51
+ // Costo: rastreamos por mensaje para no acumular en updates repetidos
52
+ const msgId = msg.id ?? msg.messageID ?? `${sid}-${Date.now()}`
53
+ msgCostTracker.set(`${sid}:${msgId}`, msg.cost ?? 0)
54
+
55
+ const cost = Array.from(msgCostTracker.entries())
56
+ .filter(([k]) => k.startsWith(`${sid}:`))
57
+ .reduce((s, [, v]) => s + v, 0)
58
+
59
+ const prev = ctxStore.get(sid) ?? { tokens: 0, cost: 0 }
60
+ ctxStore.set(sid, { tokens: Math.max(prev.tokens, tokens), cost })
61
+
62
+ setCtxTick(v => v + 1)
63
+ })
64
+
65
+ // Resolver context window real del modelo activo
66
+ const getContextLimit = (): number => {
67
+ try {
68
+ const modelStr = api.state.config?.model ?? ""
69
+ const [providerID, modelID] = modelStr.split("/")
70
+ const provider = api.state.provider.find(p => p.id === providerID)
71
+ const model = provider?.models?.[modelID]
72
+ if (model?.limit?.context) return model.limit.context
73
+ } catch (_) {}
74
+ return 1_000_000
75
+ }
76
+
28
77
  // Slot registration
29
78
  api.slots.register({
30
79
  slots: {
31
- // Home screen: large Arch logo + j0k3r-dev-rgl@latest legend
32
80
  home_logo(ctx) {
33
81
  return <HomeLogo theme={ctx.theme.current} />
34
82
  },
35
83
 
36
- // Below the prompt: environment detection
37
84
  home_bottom(ctx) {
38
85
  return <DetectedEnv theme={ctx.theme.current} providers={api.state.provider} config={boot} />
39
86
  },
40
87
 
41
- // Sidebar: Arch logo + live stats with progress bars
42
88
  sidebar_content(ctx, value) {
43
89
  const sessionID = value?.session_id
90
+
91
+ // Registrar sesión en el tracker si es nueva
92
+ if (sessionID && !ctxStore.has(sessionID)) {
93
+ ctxStore.set(sessionID, { tokens: 0, cost: 0 })
94
+ }
95
+
96
+ // Suscribirse a la señal reactiva
97
+ const _ = ctxTick()
98
+ const stats = sessionID
99
+ ? (ctxStore.get(sessionID) ?? { tokens: 0, cost: 0 })
100
+ : { tokens: 0, cost: 0 }
101
+
44
102
  const branch = api.state.vcs?.branch
45
- const files = sessionID ? api.state.session.diff(sessionID) : []
46
103
  const todos = sessionID ? api.state.session.todo(sessionID) : []
47
- const messages = sessionID ? api.state.session.messages(sessionID) : []
48
104
  const mcpItems = api.state.mcp()
49
105
  const lspItems = api.state.lsp()
50
106
 
@@ -52,18 +108,17 @@ const tui: TuiPlugin = async (api, options) => {
52
108
  <SidebarArch
53
109
  theme={ctx.theme.current}
54
110
  config={boot}
55
- sessionID={sessionID}
56
111
  branch={branch}
57
- files={files}
58
112
  todos={todos}
59
- messages={messages}
60
113
  mcpItems={mcpItems}
61
114
  lspItems={lspItems}
115
+ contextTokens={stats.tokens}
116
+ contextCost={stats.cost}
117
+ contextLimit={getContextLimit()}
62
118
  />
63
119
  )
64
120
  },
65
121
 
66
- // Prompt bar right side: session info
67
122
  session_prompt_right(ctx, value) {
68
123
  const t = ctx.theme.current
69
124
  return (