@prevalentware/opencode-goal-plugin 0.1.8 → 0.1.10

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/tui.tsx +17 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prevalentware/opencode-goal-plugin",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Codex-style long-running goal mode for OpenCode.",
5
5
  "keywords": [
6
6
  "opencode",
package/src/tui.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
2
  import type { TuiPlugin, TuiPluginApi, TuiPluginModule } from "@opencode-ai/plugin/tui"
3
- import { createMemo, createSignal, onCleanup, onMount, Show } from "solid-js"
3
+ import { createMemo, createSignal, onCleanup, Show } from "solid-js"
4
4
 
5
5
  type GoalSnapshot = {
6
6
  sessionID: string
@@ -116,24 +116,26 @@ function formatDuration(seconds: number) {
116
116
  const hours = Math.floor(total / 3600)
117
117
  const minutes = Math.floor((total % 3600) / 60)
118
118
  const secs = total % 60
119
- if (hours > 0) return `${hours}h ${minutes}m`
120
- if (minutes > 0) return `${minutes}m ${secs}s`
121
- return `${secs}s`
119
+ const paddedSecs = String(secs).padStart(2, "0")
120
+ if (hours > 0) return `${hours}:${String(minutes).padStart(2, "0")}:${paddedSecs}`
121
+ return `${minutes}:${paddedSecs}`
122
122
  }
123
123
 
124
124
  function formatDurationBadge(seconds: number) {
125
- const total = Math.max(0, Math.floor(seconds))
126
- const hours = Math.floor(total / 3600)
127
- const minutes = Math.floor((total % 3600) / 60)
128
- if (hours > 0) return `${hours}h${minutes > 0 ? ` ${minutes}m` : ""}`
129
- if (minutes > 0) return `${minutes}m`
130
- return `${total}s`
125
+ return formatDuration(seconds)
131
126
  }
132
127
 
133
- function nowSeconds() {
128
+ function currentEpochSeconds() {
134
129
  return Math.floor(Date.now() / 1000)
135
130
  }
136
131
 
132
+ export function liveTimeUsedSeconds(goal: GoalSnapshot, nowSeconds = currentEpochSeconds()) {
133
+ const baseSeconds = Math.max(0, Math.floor(goal.timeUsedSeconds))
134
+ if (visibleStatus(goal.status) !== "active") return baseSeconds
135
+ if (typeof goal.sampledAt !== "number") return baseSeconds
136
+ return baseSeconds + Math.max(0, Math.floor(nowSeconds - goal.sampledAt))
137
+ }
138
+
137
139
  function isRecord(value: unknown): value is Record<string, unknown> {
138
140
  return typeof value === "object" && value !== null
139
141
  }
@@ -191,11 +193,6 @@ function goalFromSession(api: TuiPluginApi, sessionID: string) {
191
193
  return goalStateFromSession(api, sessionID).goal
192
194
  }
193
195
 
194
- function liveTimeUsed(goal: GoalSnapshot, currentSeconds: number) {
195
- if (visibleStatus(goal.status) !== "active" || goal.sampledAt == null) return goal.timeUsedSeconds
196
- return goal.timeUsedSeconds + Math.max(0, currentSeconds - goal.sampledAt)
197
- }
198
-
199
196
  function visibleStatus(status: GoalSnapshot["status"]) {
200
197
  return status === "budgetLimited" ? "active" : status
201
198
  }
@@ -214,11 +211,9 @@ function formatGoal(goal: GoalSnapshot | null) {
214
211
 
215
212
  function GoalSidebar(props: { api: TuiPluginApi; sessionID: string }) {
216
213
  const theme = () => props.api.theme.current
217
- const [currentSeconds, setCurrentSeconds] = createSignal(nowSeconds())
218
- onMount(() => {
219
- const interval = setInterval(() => setCurrentSeconds(nowSeconds()), 1000)
220
- onCleanup(() => clearInterval(interval))
221
- })
214
+ const [nowSeconds, setNowSeconds] = createSignal(currentEpochSeconds())
215
+ const timer = setInterval(() => setNowSeconds(currentEpochSeconds()), 1000)
216
+ onCleanup(() => clearInterval(timer))
222
217
  const state = createMemo(() => {
223
218
  props.api.state.session.messages(props.sessionID)
224
219
  return goalStateFromSession(props.api, props.sessionID)
@@ -226,7 +221,7 @@ function GoalSidebar(props: { api: TuiPluginApi; sessionID: string }) {
226
221
  const goal = createMemo(() => state().goal)
227
222
  const elapsed = createMemo(() => {
228
223
  const value = goal()
229
- return value ? liveTimeUsed(value, currentSeconds()) : 0
224
+ return value ? liveTimeUsedSeconds(value, nowSeconds()) : 0
230
225
  })
231
226
  const objective = createMemo(() => {
232
227
  const value = goal()?.objective ?? ""