opencode-mask-j0k3r-dev-rgl 2.0.20 → 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 +8 -14
  2. package/package.json +1 -1
  3. package/tui.tsx +68 -26
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,14 +76,13 @@ 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>
87
- contextLimit?: number
83
+ contextTokens: number
84
+ contextCost: number
85
+ contextLimit: number
88
86
  }) => {
89
87
  if (!props.config.show_sidebar) return null
90
88
 
@@ -107,14 +105,10 @@ export const SidebarArch = (props: {
107
105
  const lspActive = lspItems.filter(l => l.status === "idle" || l.status === "running").length
108
106
  const lspTotal = lspItems.length
109
107
 
110
- // ── Tokens & Cost ─────────────────────────────────────────────────────────
111
- const messages = props.messages ?? []
112
- const assistantMsgs = messages.filter(m => m.role === "assistant")
113
- const contextTokens = assistantMsgs.reduce((max, m) => Math.max(max, m.tokens?.input ?? 0), 0)
114
- const totalCost = assistantMsgs.reduce((s, m) => s + (m.cost ?? 0), 0)
115
-
116
- // context window real del modelo via api.state.provider → model.limit.context
117
- const contextLimit = props.contextLimit ?? 1_000_000
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
118
112
  const contextPct = Math.min(100, Math.round((contextTokens / contextLimit) * 100))
119
113
  const costPct = Math.min(100, Math.round(totalCost * 100))
120
114
 
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.20",
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,68 +16,109 @@ 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
 
51
- // Resolver el context window real del modelo activo
52
- // api.state.config.model = "providerID/modelID"
53
- // api.state.provider = ReadonlyArray<Provider> donde Provider.models[modelID].limit.context
54
- let contextLimit: number | undefined
55
- try {
56
- const modelStr = api.state.config?.model ?? ""
57
- const [providerID, modelID] = modelStr.split("/")
58
- const provider = api.state.provider.find(p => p.id === providerID)
59
- const model = provider?.models?.[modelID]
60
- if (model?.limit?.context) contextLimit = model.limit.context
61
- } catch (_) {}
62
-
63
107
  return (
64
108
  <SidebarArch
65
109
  theme={ctx.theme.current}
66
110
  config={boot}
67
- sessionID={sessionID}
68
111
  branch={branch}
69
- files={files}
70
112
  todos={todos}
71
- messages={messages}
72
113
  mcpItems={mcpItems}
73
114
  lspItems={lspItems}
74
- contextLimit={contextLimit}
115
+ contextTokens={stats.tokens}
116
+ contextCost={stats.cost}
117
+ contextLimit={getContextLimit()}
75
118
  />
76
119
  )
77
120
  },
78
121
 
79
- // Prompt bar right side: session info
80
122
  session_prompt_right(ctx, value) {
81
123
  const t = ctx.theme.current
82
124
  return (