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.
- package/components.tsx +8 -14
- package/package.json +1 -1
- 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,
|
|
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
|
-
|
|
87
|
-
|
|
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
|
-
// ──
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
const
|
|
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.
|
|
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
|
|
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
|
-
|
|
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 (
|