opencode-mask-j0k3r-dev-rgl 2.0.22 → 2.0.23

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/ascii-frames.ts CHANGED
@@ -47,9 +47,9 @@ export const homeLogoZones: ("hotPink" | "white")[] = [
47
47
  "hotPink", // line 6: `/:-:++oooo+:
48
48
  "hotPink", // line 7: `/++++/+++++++:
49
49
  "hotPink", // line 8: `/+++++++++++++++:
50
- "white", // line 9: `/+++ooooooooooooo/`
51
- "white", // line 10: ./ooosssso++osssssso+`
52
- "white", // line 11: .oossssso-````/ossssss+`
50
+ "white", // line 9: `/+++ooooooooooooo/`
51
+ "white", // line 10: ./ooosssso++osssssso+`
52
+ "white", // line 11: .oossssso-````/ossssss+`
53
53
  "hotPink", // line 12: -osssssso. :ssssssso.
54
54
  "hotPink", // line 13: :osssssss/ osssso+++.
55
55
  "hotPink", // line 14: /ossssssss/ +ssssooo/-
@@ -61,14 +61,14 @@ export const homeLogoZones: ("hotPink" | "white")[] = [
61
61
 
62
62
  // ─── Sidebar logo (mini Arch) ────────────────────────────────────────────────
63
63
  export const archLogoSidebar: string[] = [
64
- ' /\\\\ ',
65
- ' / \\\\ ',
66
- ' / \\\\ ',
67
- ' / \\\\ ',
68
- ' / ,, \\\\ ',
69
- ' / | | \\\\ ',
70
- ' / /-\"\"-\\\\ \\\\ ',
71
- '/___/ \\\\___\\\\',
64
+ " /\\ ",
65
+ " / \\ ",
66
+ " / \\ ",
67
+ " / \\ ",
68
+ " / ,, \\ ",
69
+ " / | | \\ ",
70
+ ' / /-""-\\ \\ ',
71
+ "/___/ \\___\\",
72
72
  ];
73
73
 
74
74
  export const sidebarLogoZones: ("hotPink" | "white")[] = [
package/components.tsx CHANGED
@@ -1,224 +1,240 @@
1
1
  // @ts-nocheck
2
2
  /** @jsxImportSource @opentui/solid */
3
- import type { TuiThemeCurrent, TuiSidebarMcpItem, TuiSidebarLspItem, TuiSidebarTodoItem } from "@opencode-ai/plugin/tui"
4
- import type { Cfg } from "./config"
5
- import { getOSName, getProviders } from "./detection"
3
+ import type { TuiThemeCurrent } from "@opencode-ai/plugin/tui";
6
4
  import {
7
- archLogoHome,
8
- homeLogoZones,
9
- archLogoSidebar,
10
- sidebarLogoZones,
11
- zoneColors,
12
- } from "./ascii-frames"
5
+ archLogoHome,
6
+ archLogoSidebar,
7
+ homeLogoZones,
8
+ sidebarLogoZones,
9
+ zoneColors,
10
+ } from "./ascii-frames";
11
+ import type { Cfg } from "./config";
12
+ import { getOSName, getProviders } from "./detection";
13
13
 
14
14
  // ─── Home screen: Large Arch Linux logo + legend ─────────────────────────────
15
15
  export const HomeLogo = (props: { theme: TuiThemeCurrent }) => {
16
- const t = props.theme
17
-
18
- return (
19
- <box flexDirection="column" alignItems="center">
20
- {archLogoHome.map((line, i) => {
21
- const zone = homeLogoZones[i]
22
- const color = zoneColors[zone] || t.primary
23
- return <text fg={color}>{line}</text>
24
- })}
25
-
26
- <text> </text>
27
- <box flexDirection="row" gap={0}>
28
- <text fg="#ff2d78" bold={true}>j0k3r</text>
29
- <text fg="#9d4edd">-</text>
30
- <text fg="#00c8ff" bold={true}>dev</text>
31
- <text fg="#9d4edd">-</text>
32
- <text fg="#4dd8ff" bold={true}>rgl</text>
33
- <text fg="#9d4edd">@</text>
34
- <text fg="#ffd166" bold={true}>latest</text>
35
- </box>
36
-
37
- <box flexDirection="row" gap={0} marginTop={1}>
38
- <text fg={t.textMuted} dimColor={true}>╭ </text>
39
- <text fg={t.textMuted}>arch linux </text>
40
- <text fg={t.textMuted} dimColor={true}>·</text>
41
- <text fg={t.textMuted}> opencode </text>
42
- <text fg={t.textMuted} dimColor={true}>╮</text>
43
- </box>
44
-
45
- <text> </text>
46
- </box>
47
- )
48
- }
16
+ const t = props.theme;
17
+
18
+ return (
19
+ <box flexDirection="column" alignItems="center">
20
+ {archLogoHome.map((line, i) => {
21
+ const zone = homeLogoZones[i];
22
+ const color = zoneColors[zone] || t.primary;
23
+ return <text fg={color}>{line}</text>;
24
+ })}
25
+
26
+ <text> </text>
27
+ <box flexDirection="row" gap={0}>
28
+ <text fg="#ff2d78" bold={true}>
29
+ j0k3r
30
+ </text>
31
+ <text fg="#9d4edd">-</text>
32
+ <text fg="#00c8ff" bold={true}>
33
+ dev
34
+ </text>
35
+ <text fg="#9d4edd">-</text>
36
+ <text fg="#4dd8ff" bold={true}>
37
+ rgl
38
+ </text>
39
+ <text fg="#9d4edd">@</text>
40
+ <text fg="#ffd166" bold={true}>
41
+ latest
42
+ </text>
43
+ </box>
44
+
45
+ <box flexDirection="row" gap={0} marginTop={1}>
46
+ <text fg={t.textMuted} dimColor={true}>
47
+ ╭{" "}
48
+ </text>
49
+ <text fg={t.textMuted}>arch linux </text>
50
+ <text fg={t.textMuted} dimColor={true}>
51
+ ·
52
+ </text>
53
+ <text fg={t.textMuted}> opencode </text>
54
+ <text fg={t.textMuted} dimColor={true}>
55
+
56
+ </text>
57
+ </box>
58
+
59
+ <text> </text>
60
+ </box>
61
+ );
62
+ };
49
63
 
50
64
  // ─── Progress bar helper ──────────────────────────────────────────────────────
51
65
  const ProgressBar = (props: {
52
- value: number // 0–100
53
- width?: number
54
- fillColor: string
55
- emptyColor: string
56
- theme: TuiThemeCurrent
66
+ value: number; // 0–100
67
+ width?: number;
68
+ fillColor: string;
69
+ emptyColor: string;
70
+ theme: TuiThemeCurrent;
57
71
  }) => {
58
- const width = props.width ?? 12
59
- const pct = Math.max(0, Math.min(100, props.value))
60
- const filled = Math.round((pct / 100) * width)
61
- const empty = width - filled
62
- const fill = "█".repeat(filled)
63
- const trail = "░".repeat(empty)
64
-
65
- return (
66
- <box flexDirection="row" gap={0}>
67
- <text fg={props.theme.textMuted}>[</text>
68
- <text fg={props.fillColor}>{fill}</text>
69
- <text fg={props.emptyColor}>{trail}</text>
70
- <text fg={props.theme.textMuted}>]</text>
71
- </box>
72
- )
73
- }
72
+ const width = props.width ?? 12;
73
+ const pct = Math.max(0, Math.min(100, props.value));
74
+ const filled = Math.round((pct / 100) * width);
75
+ const empty = width - filled;
76
+ const fill = "█".repeat(filled);
77
+ const trail = "░".repeat(empty);
78
+
79
+ return (
80
+ <box flexDirection="row" gap={0}>
81
+ <text fg={props.theme.textMuted}>[</text>
82
+ <text fg={props.fillColor}>{fill}</text>
83
+ <text fg={props.emptyColor}>{trail}</text>
84
+ <text fg={props.theme.textMuted}>]</text>
85
+ </box>
86
+ );
87
+ };
74
88
 
75
89
  // ─── Sidebar: Arch logo + stats panel ────────────────────────────────────────
76
90
  export const SidebarArch = (props: {
77
- theme: TuiThemeCurrent
78
- config: Cfg
79
- branch?: string
80
- mcpItems?: ReadonlyArray<TuiSidebarMcpItem>
81
- lspItems?: ReadonlyArray<TuiSidebarLspItem>
82
- todos?: ReadonlyArray<TuiSidebarTodoItem>
83
- contextTokens: number
84
- contextCost: number
85
- contextLimit: number
91
+ theme: TuiThemeCurrent;
92
+ config: Cfg;
93
+ branch?: string;
94
+ getMessages?: () => any[];
95
+ contextLimit: number;
86
96
  }) => {
87
- if (!props.config.show_sidebar) return null
88
-
89
- const t = props.theme
90
-
91
- // ── Todos ─────────────────────────────────────────────────────────────────
92
- const todos = props.todos ?? []
93
- const doneTodos = todos.filter(t => t.status === "completed").length
94
- const totalTodos = todos.length
95
- const todoPct = totalTodos > 0 ? Math.round((doneTodos / totalTodos) * 100) : 0
96
-
97
- // ── MCP ───────────────────────────────────────────────────────────────────
98
- const mcpItems = props.mcpItems ?? []
99
- const mcpConnected = mcpItems.filter(m => m.status === "connected").length
100
- const mcpTotal = mcpItems.length
101
- const mcpPct = mcpTotal > 0 ? Math.round((mcpConnected / mcpTotal) * 100) : 0
102
-
103
- // ── LSP ───────────────────────────────────────────────────────────────────
104
- const lspItems = props.lspItems ?? []
105
- const lspActive = lspItems.filter(l => l.status === "idle" || l.status === "running").length
106
- const lspTotal = lspItems.length
107
-
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))
113
- const costPct = Math.min(100, Math.round(totalCost * 100))
114
-
115
- const fmtTokens = (n: number) => n >= 1000 ? `${(n / 1000).toFixed(1)}k` : `${n}`
116
- const fmtCost = (n: number) => `$${n.toFixed(2)}`
117
-
118
- // Color: verde → amarillo → rojo según el %
119
- const ctxColor = contextPct < 50 ? "#00e5a0" : contextPct < 80 ? "#ffd166" : "#ff2d78"
120
-
121
- return (
122
- <box flexDirection="column" alignItems="center">
123
-
124
- {/* Mini Arch logo */}
125
- {archLogoSidebar.map((line, i) => {
126
- const zone = sidebarLogoZones[i]
127
- const color = zoneColors[zone] || t.primary
128
- return <text fg={color}>{line}</text>
129
- })}
130
-
131
- <text fg={t.textMuted}>j0k3r@latest</text>
132
- <text> </text>
133
-
134
- {/* Git branch */}
135
- {props.branch && (
136
- <box flexDirection="row" gap={1}>
137
- <text fg="#ffd166">⎇</text>
138
- <text fg={t.text}>{props.branch}</text>
139
- </box>
140
- )}
141
-
142
- {/* ── Context (tokens + % used + cost) ── siempre visible */}
143
- <box flexDirection="column" alignItems="center" marginTop={1}>
144
- <text fg={t.textMuted} bold={true}>Context</text>
145
-
146
- {/* tokens */}
147
- <box flexDirection="row" gap={1}>
148
- <text fg={t.text}>{fmtTokens(contextTokens)}</text>
149
- <text fg={t.textMuted}>tokens</text>
150
- </box>
151
- <ProgressBar value={contextPct} width={18} fillColor={ctxColor} emptyColor="#3a3a3a" theme={t} />
152
-
153
- {/* % used */}
154
- <box flexDirection="row" gap={1}>
155
- <text fg={ctxColor}>{contextPct}%</text>
156
- <text fg={t.textMuted}>used</text>
157
- </box>
158
- <ProgressBar value={contextPct} width={18} fillColor={ctxColor} emptyColor="#3a3a3a" theme={t} />
159
-
160
- {/* $ spent */}
161
- <box flexDirection="row" gap={1}>
162
- <text fg="#ffd166">{fmtCost(totalCost)}</text>
163
- <text fg={t.textMuted}>spent</text>
164
- </box>
165
- <ProgressBar value={costPct} width={18} fillColor="#ffd166" emptyColor="#3a3a3a" theme={t} />
166
- </box>
167
-
168
- {/* ── Todos ── */}
169
- {totalTodos > 0 && (
170
- <box flexDirection="column" marginTop={1}>
171
- <box flexDirection="row" gap={1}>
172
- <text fg={t.textMuted}>todos</text>
173
- <text fg={t.text}>{doneTodos}/{totalTodos}</text>
174
- </box>
175
- <ProgressBar value={todoPct} width={12} fillColor="#9d4edd" emptyColor="#3a3a3a" theme={t} />
176
- </box>
177
- )}
178
-
179
- {/* ── MCP ── */}
180
- {mcpTotal > 0 && (
181
- <box flexDirection="column" marginTop={1}>
182
- <box flexDirection="row" gap={1}>
183
- <text fg={t.textMuted}>mcp</text>
184
- <text fg={t.text}>{mcpConnected}/{mcpTotal}</text>
185
- </box>
186
- <ProgressBar value={mcpPct} width={12} fillColor="#00c8ff" emptyColor="#3a3a3a" theme={t} />
187
- </box>
188
- )}
189
-
190
- {/* ── LSP ── */}
191
- {lspTotal > 0 && (
192
- <box flexDirection="row" gap={1} marginTop={1}>
193
- <text fg={t.textMuted}>lsp</text>
194
- <text fg={lspActive > 0 ? "#00e5a0" : "#555555"}>{lspActive}/{lspTotal}</text>
195
- </box>
196
- )}
197
-
198
- <text> </text>
199
- </box>
200
- )
201
- }
97
+ if (!props.config.show_sidebar) return null;
98
+
99
+ const t = props.theme;
100
+
101
+ // ── Context — Reactivo y con la misma lógica interna de OpenCode ──────────
102
+ const getContextTokens = () => {
103
+ const messages = props.getMessages ? props.getMessages() : [];
104
+ // Buscar el último mensaje de asistente que tuvo output de tokens
105
+ const last = [...messages]
106
+ .reverse()
107
+ .find((m: any) => m.role === "assistant" && m.tokens?.output > 0);
108
+ if (!last) return 0;
109
+
110
+ // Suma total de todos los tokens del contexto de ese mensaje exacto
111
+ const tk = last.tokens;
112
+ return (
113
+ (tk.input ?? 0) +
114
+ (tk.output ?? 0) +
115
+ (tk.reasoning ?? 0) +
116
+ (tk.cache?.read ?? 0) +
117
+ (tk.cache?.write ?? 0)
118
+ );
119
+ };
120
+
121
+ const getTotalCost = () => {
122
+ const messages = props.getMessages ? props.getMessages() : [];
123
+ return messages.reduce(
124
+ (sum, item) => sum + (item.role === "assistant" ? (item.cost ?? 0) : 0),
125
+ 0,
126
+ );
127
+ };
128
+
129
+ const getContextPct = () => {
130
+ const limit = props.contextLimit || 1_000_000;
131
+ return Math.min(100, Math.round((getContextTokens() / limit) * 100));
132
+ };
133
+
134
+ const getCostPct = () => Math.min(100, Math.round(getTotalCost() * 100));
135
+
136
+ const fmtTokens = (n: number) =>
137
+ n >= 1000 ? `${(n / 1000).toFixed(1)}k` : `${n}`;
138
+ const fmtCost = (n: number) => `$${n.toFixed(2)}`;
139
+
140
+ // Color: verde → amarillo → rojo según el %
141
+ const getCtxColor = () => {
142
+ const pct = getContextPct();
143
+ return pct < 50 ? "#00e5a0" : pct < 80 ? "#ffd166" : "#ff2d78";
144
+ };
145
+
146
+ return (
147
+ <box flexDirection="column" alignItems="center">
148
+ {/* Mini Arch logo */}
149
+ {archLogoSidebar.map((line, i) => {
150
+ const zone = sidebarLogoZones[i];
151
+ const color = zoneColors[zone] || t.primary;
152
+ return <text fg={color}>{line}</text>;
153
+ })}
154
+
155
+ <text fg={t.textMuted}>j0k3r@latest</text>
156
+ <text> </text>
157
+
158
+ {/* Git branch */}
159
+ {props.branch && (
160
+ <box flexDirection="row" gap={1}>
161
+ <text fg="#ffd166">⎇</text>
162
+ <text fg={t.text}>{props.branch}</text>
163
+ </box>
164
+ )}
165
+
166
+ {/* ── Context (tokens + % used + cost) ── siempre visible */}
167
+ <box flexDirection="column" alignItems="center" marginTop={1}>
168
+ <text fg={t.textMuted} bold={true}>
169
+ Context
170
+ </text>
171
+
172
+ {/* tokens */}
173
+ <box flexDirection="row" gap={1}>
174
+ <text fg={t.text}>{fmtTokens(getContextTokens())}</text>
175
+ <text fg={t.textMuted}>tokens</text>
176
+ </box>
177
+ <ProgressBar
178
+ value={getContextPct()}
179
+ width={18}
180
+ fillColor={getCtxColor()}
181
+ emptyColor="#3a3a3a"
182
+ theme={t}
183
+ />
184
+
185
+ {/* % used */}
186
+ <box flexDirection="row" gap={1}>
187
+ <text fg={getCtxColor()}>{getContextPct()}%</text>
188
+ <text fg={t.textMuted}>used</text>
189
+ </box>
190
+ <ProgressBar
191
+ value={getContextPct()}
192
+ width={18}
193
+ fillColor={getCtxColor()}
194
+ emptyColor="#3a3a3a"
195
+ theme={t}
196
+ />
197
+
198
+ {/* $ spent */}
199
+ <box flexDirection="row" gap={1}>
200
+ <text fg="#ffd166">{fmtCost(getTotalCost())}</text>
201
+ <text fg={t.textMuted}>spent</text>
202
+ </box>
203
+ <ProgressBar
204
+ value={getCostPct()}
205
+ width={18}
206
+ fillColor="#ffd166"
207
+ emptyColor="#3a3a3a"
208
+ theme={t}
209
+ />
210
+ </box>
211
+
212
+ <text> </text>
213
+ </box>
214
+ );
215
+ };
202
216
 
203
217
  // ─── Environment detection line ──────────────────────────────────────────────
204
218
  export const DetectedEnv = (props: {
205
- theme: TuiThemeCurrent
206
- providers: ReadonlyArray<{ id: string; name: string }> | undefined
207
- config: Cfg
219
+ theme: TuiThemeCurrent;
220
+ providers: ReadonlyArray<{ id: string; name: string }> | undefined;
221
+ config: Cfg;
208
222
  }) => {
209
- if (!props.config.show_detected) return null
210
-
211
- const os = props.config.show_os ? getOSName() : null
212
- const providers = props.config.show_providers ? getProviders(props.providers) : null
213
-
214
- if (!os && !providers) return null
215
-
216
- return (
217
- <box flexDirection="row" gap={1}>
218
- <text fg={props.theme.textMuted}>detected:</text>
219
- {os && <text fg={props.theme.text}>{os}</text>}
220
- {os && providers && <text fg={props.theme.textMuted}>·</text>}
221
- {providers && <text fg={props.theme.text}>{providers}</text>}
222
- </box>
223
- )
224
- }
223
+ if (!props.config.show_detected) return null;
224
+
225
+ const os = props.config.show_os ? getOSName() : null;
226
+ const providers = props.config.show_providers
227
+ ? getProviders(props.providers)
228
+ : null;
229
+
230
+ if (!os && !providers) return null;
231
+
232
+ return (
233
+ <box flexDirection="row" gap={1}>
234
+ <text fg={props.theme.textMuted}>detected:</text>
235
+ {os && <text fg={props.theme.text}>{os}</text>}
236
+ {os && providers && <text fg={props.theme.textMuted}>·</text>}
237
+ {providers && <text fg={props.theme.text}>{providers}</text>}
238
+ </box>
239
+ );
240
+ };
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.22",
4
+ "version": "2.0.23",
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,148 +1,108 @@
1
1
  // @ts-nocheck
2
2
  /** @jsxImportSource @opentui/solid */
3
- import type { TuiPlugin, TuiPluginModule } from "@opencode-ai/plugin/tui"
4
- import { createSignal } from "solid-js"
5
- import { cfg } from "./config"
6
- import { HomeLogo, SidebarArch, DetectedEnv } from "./components"
3
+ import type { TuiPlugin, TuiPluginModule } from "@opencode-ai/plugin/tui";
4
+ import { DetectedEnv, HomeLogo, SidebarArch } from "./components";
5
+ import { cfg } from "./config";
7
6
 
8
- const id = "j0k3r-dev-rgl"
7
+ const id = "j0k3r-dev-rgl";
9
8
 
10
9
  const rec = (value: unknown) => {
11
- if (!value || typeof value !== "object" || Array.isArray(value)) return
12
- return Object.fromEntries(Object.entries(value))
13
- }
10
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
11
+ return Object.fromEntries(Object.entries(value));
12
+ };
14
13
 
15
14
  const tui: TuiPlugin = async (api, options) => {
16
- const boot = cfg(rec(options))
17
- if (!boot.enabled) return
18
-
19
- // Theme setup
20
- try {
21
- await api.theme.install("./themes/j0k3r-dev-rgl.json")
22
- if (boot.set_theme) api.theme.set(boot.theme)
23
- } catch (error) {
24
- console.error("[j0k3r-dev-rgl] Theme setup failed:", error)
25
- }
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
- let debugLogged = false
36
- api.event.on("message.updated", (event) => {
37
- // Debug: loguear estructura completa la primera vez
38
- if (!debugLogged) {
39
- debugLogged = true
40
- const propsKeys = Object.keys((event as any).properties ?? {}).join(",")
41
- const infoKeys = Object.keys(((event as any).properties?.info ?? (event as any).properties) ?? {}).join(",")
42
- console.error("[j0k3r-mask] event.properties keys:", propsKeys)
43
- console.error("[j0k3r-mask] info/msg keys:", infoKeys)
44
- console.error("[j0k3r-mask] full event:", JSON.stringify(event).slice(0, 800))
45
- api.ui.toast({ variant: "info", message: `props: ${propsKeys} | info: ${infoKeys}`, duration: 8000 })
46
- }
47
-
48
- // Intentar ambas estructuras: { properties: { info: Message } } o { properties: Message }
49
- const props = (event as any).properties
50
- const msg = props?.info ?? props
51
- if (!msg || msg.role !== "assistant") return
52
-
53
- const sid: string | undefined = msg.sessionID
54
- if (!sid) return
55
-
56
- const t = msg.tokens ?? {}
57
- const tokens = t.input ?? 0
58
- const msgId: string = msg.id ?? `${sid}-unknown`
59
- msgCostTracker.set(`${sid}:${msgId}`, msg.cost ?? 0)
60
-
61
- const cost = Array.from(msgCostTracker.entries())
62
- .filter(([k]) => k.startsWith(`${sid}:`))
63
- .reduce((s, [, v]) => s + v, 0)
64
-
65
- const prev = ctxStore.get(sid) ?? { tokens: 0, cost: 0 }
66
- ctxStore.set(sid, { tokens: Math.max(prev.tokens, tokens), cost })
67
-
68
- console.error(`[j0k3r-mask] sid=${sid} tokens=${tokens} cost=${cost}`)
69
- setCtxTick(v => v + 1)
70
- })
71
-
72
- // Resolver context window real del modelo activo
73
- const getContextLimit = (): number => {
74
- try {
75
- const modelStr = api.state.config?.model ?? ""
76
- const [providerID, modelID] = modelStr.split("/")
77
- const provider = api.state.provider.find(p => p.id === providerID)
78
- const model = provider?.models?.[modelID]
79
- if (model?.limit?.context) return model.limit.context
80
- } catch (_) {}
81
- return 1_000_000
82
- }
83
-
84
- // Slot registration
85
- api.slots.register({
86
- slots: {
87
- home_logo(ctx) {
88
- return <HomeLogo theme={ctx.theme.current} />
89
- },
90
-
91
- home_bottom(ctx) {
92
- return <DetectedEnv theme={ctx.theme.current} providers={api.state.provider} config={boot} />
93
- },
94
-
95
- sidebar_content(ctx, value) {
96
- const sessionID = value?.session_id
97
-
98
- // Registrar sesión en el tracker si es nueva
99
- if (sessionID && !ctxStore.has(sessionID)) {
100
- ctxStore.set(sessionID, { tokens: 0, cost: 0 })
101
- }
102
-
103
- // Suscribirse a la señal reactiva
104
- const _ = ctxTick()
105
- const stats = sessionID
106
- ? (ctxStore.get(sessionID) ?? { tokens: 0, cost: 0 })
107
- : { tokens: 0, cost: 0 }
108
-
109
- const branch = api.state.vcs?.branch
110
- const todos = sessionID ? api.state.session.todo(sessionID) : []
111
- const mcpItems = api.state.mcp()
112
- const lspItems = api.state.lsp()
113
-
114
- return (
115
- <SidebarArch
116
- theme={ctx.theme.current}
117
- config={boot}
118
- branch={branch}
119
- todos={todos}
120
- mcpItems={mcpItems}
121
- lspItems={lspItems}
122
- contextTokens={stats.tokens}
123
- contextCost={stats.cost}
124
- contextLimit={getContextLimit()}
125
- />
126
- )
127
- },
128
-
129
- session_prompt_right(ctx, value) {
130
- const t = ctx.theme.current
131
- return (
132
- <text fg={t.textMuted}>
133
- <span style={{ fg: "#ff2d78" }}>j0k3r</span>
134
- <span style={{ fg: "#9d4edd" }}>@</span>
135
- {(value.session_id ?? "").slice(0, 6)}
136
- </text>
137
- )
138
- },
139
- },
140
- })
141
- }
15
+ const boot = cfg(rec(options));
16
+ if (!boot.enabled) return;
17
+
18
+ // Theme setup
19
+ try {
20
+ await api.theme.install("./themes/j0k3r-dev-rgl.json");
21
+ if (boot.set_theme) api.theme.set(boot.theme);
22
+ } catch (error) {
23
+ // Silent fail
24
+ }
25
+
26
+ // Asegurarnos de que los plugins internos estén ACTIVOS
27
+ // (los reactivamos por si quedaron apagados de la sesión anterior)
28
+ const enableInternal = async () => {
29
+ try {
30
+ await api.plugins.deactivate("internal:sidebar-context");
31
+ await api.plugins.deactivate("internal:sidebar-mcp");
32
+ await api.plugins.activate("internal:sidebar-lsp");
33
+ await api.plugins.activate("internal:sidebar-todo");
34
+ await api.plugins.activate("internal:sidebar-files");
35
+ } catch (e) {
36
+ // Silent fail
37
+ }
38
+ };
39
+ enableInternal();
40
+
41
+ // Resolver context window real del modelo activo
42
+ const getContextLimit = (): number => {
43
+ try {
44
+ const modelStr = api.state.config?.model ?? "";
45
+ const [providerID, modelID] = modelStr.split("/");
46
+ const provider = api.state.provider.find((p) => p.id === providerID);
47
+ const model = provider?.models?.[modelID];
48
+ if (model?.limit?.context) return model.limit.context;
49
+ } catch (_) {}
50
+ return 1_000_000;
51
+ };
52
+
53
+ // Slot registration
54
+ api.slots.register({
55
+ slots: {
56
+ home_logo(ctx) {
57
+ return <HomeLogo theme={ctx.theme.current} />;
58
+ },
59
+
60
+ home_bottom(ctx) {
61
+ return (
62
+ <DetectedEnv
63
+ theme={ctx.theme.current}
64
+ providers={api.state.provider}
65
+ config={boot}
66
+ />
67
+ );
68
+ },
69
+
70
+ sidebar_content(ctx, value) {
71
+ const sessionID = value?.session_id;
72
+
73
+ const branch = api.state.vcs?.branch;
74
+ const contextLimit = getContextLimit();
75
+
76
+ return (
77
+ <SidebarArch
78
+ theme={ctx.theme.current}
79
+ config={boot}
80
+ branch={branch}
81
+ getMessages={() =>
82
+ sessionID ? api.state.session.messages(sessionID) : []
83
+ }
84
+ contextLimit={contextLimit}
85
+ />
86
+ );
87
+ },
88
+
89
+ session_prompt_right(ctx, value) {
90
+ const t = ctx.theme.current;
91
+ return (
92
+ <text fg={t.textMuted}>
93
+ <span style={{ fg: "#ff2d78" }}>j0k3r</span>
94
+ <span style={{ fg: "#9d4edd" }}>@</span>
95
+ {(value.session_id ?? "").slice(0, 6)}
96
+ </text>
97
+ );
98
+ },
99
+ },
100
+ });
101
+ };
142
102
 
143
103
  const plugin: TuiPluginModule & { id: string } = {
144
- id,
145
- tui,
146
- }
104
+ id,
105
+ tui,
106
+ };
147
107
 
148
- export default plugin
108
+ export default plugin;