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.
- package/components.tsx +9 -25
- package/package.json +1 -1
- 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,
|
|
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
|
-
|
|
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
|
-
// ──
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
const
|
|
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.
|
|
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
|
|
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 (
|