oc-deepseek-pricing 0.0.0-stage → 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 scicco
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,212 @@
1
- # Temporary Holding Version
1
+ # oc-deepseek-pricing
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An OpenCode v2 TUI plugin that displays the current DeepSeek API pricing window in the prompt footer and session sidebar.
4
+
5
+ The indicator is shown when the selected model ID contains `deepseek`. DeepSeek off-peak rates are half of peak rates.
6
+
7
+ ## Features
8
+
9
+ - Displays the current pricing window in the prompt footer.
10
+ - Displays a compact pricing status in the session sidebar.
11
+ - Exposes complete pricing details through `/deepseek-pricing`.
12
+ - Distinguishes peak and off-peak periods with whale emoji indicators.
13
+ - Shows the next pricing transition and a countdown.
14
+ - Treats weekends as fully off-peak.
15
+ - Supports Chinese public-holiday overrides.
16
+ - Warns when the holiday calendar does not cover the current Chinese calendar year.
17
+ - Uses local computation only; no network request is needed at runtime.
18
+
19
+ ## Pricing schedule
20
+
21
+ Peak windows are evaluated in UTC:
22
+
23
+ - 01:00-04:00 UTC, Monday through Friday;
24
+ - 06:00-10:00 UTC, Monday through Friday.
25
+
26
+ All other times are off-peak, including:
27
+
28
+ - 04:00-06:00 UTC on weekdays;
29
+ - all hours after 10:00 UTC and before 01:00 UTC;
30
+ - Saturdays and Sundays in full;
31
+ - Chinese public holidays in full.
32
+
33
+ The off-peak price multiplier is 0.5.
34
+
35
+ ## Installation
36
+
37
+ Install the published plugin directly with the OpenCode CLI:
38
+
39
+ ```sh
40
+ opencode plugin add oc-deepseek-pricing
41
+ ```
42
+
43
+ To install this exact release:
44
+
45
+ ```sh
46
+ opencode plugin add oc-deepseek-pricing@0.1.0
47
+ ```
48
+
49
+ OpenCode registers the plugin automatically. No manual change to `cli.json`, `opencode.json`, or `tui.jsonc` is required.
50
+
51
+ Verify the installation with:
52
+
53
+ ```sh
54
+ opencode plugin list
55
+ ```
56
+
57
+ ## Requirements
58
+
59
+ - OpenCode v2 with TUI plugin support;
60
+ - a terminal supported by OpenCode.
61
+
62
+ Development dependencies include:
63
+
64
+ - `@opencode/plugin`;
65
+ - `@opentui/core`;
66
+ - `@opentui/solid`;
67
+ - `solid-js`;
68
+ - TypeScript.
69
+
70
+ ## Repository structure
71
+
72
+ ```text
73
+ oc-deepseek-pricing/
74
+ ├── CALENDAR_UPDATE.md
75
+ ├── LICENSE
76
+ ├── README.md
77
+ ├── bun.lock
78
+ ├── package.json
79
+ ├── src/
80
+ │ ├── holidays.ts
81
+ │ ├── pricing.test.ts
82
+ │ ├── pricing.ts
83
+ │ └── tui.tsx
84
+ ├── tsconfig.json
85
+ └── tui.ts
86
+ ```
87
+
88
+ ## Development setup
89
+
90
+ Install dependencies:
91
+
92
+ ```sh
93
+ bun install
94
+ ```
95
+
96
+ Run all validation checks:
97
+
98
+ ```sh
99
+ bun run check
100
+ ```
101
+
102
+ Run the test suite:
103
+
104
+ ```sh
105
+ bun test
106
+ ```
107
+
108
+ A successful validation completes without formatting, lint, TypeScript, or test errors.
109
+
110
+ ## Restarting OpenCode
111
+
112
+ After changing the plugin source or configuration, close the OpenCode TUI completely and start it again.
113
+
114
+ Opening a new conversation is not necessarily sufficient because plugin entrypoints are loaded during TUI startup.
115
+
116
+ ## Expected interface
117
+
118
+ When a DeepSeek model is selected during an off-peak window, the prompt and home footers display:
119
+
120
+ ```text
121
+ 🐋💤 off-peak · 9h 13m left
122
+ ```
123
+
124
+ During a peak window, they display:
125
+
126
+ ```text
127
+ 🐳 peak · 1h 15m left
128
+ ```
129
+
130
+ The session sidebar displays only the current status and remaining time:
131
+
132
+ ```text
133
+ Status 🐋💤 off-peak
134
+ 9h 13m left
135
+ ```
136
+
137
+ During a peak window, the sidebar displays:
138
+
139
+ ```text
140
+ Status 🐳 peak
141
+ 1h 15m left
142
+ ```
143
+
144
+ Run `/deepseek-pricing` to display the complete pricing details:
145
+
146
+ ```text
147
+ DeepSeek pricing
148
+ Status 🐋💤 off-peak
149
+ Reason outside peak windows
150
+ Next peak in 9h 13m
151
+ At 10/28/2026, 2:00:00 AM
152
+ China 2026-10-27
153
+ ```
154
+
155
+ The `Next` line intentionally uses the state name without an emoji. If the Chinese public-holiday calendar does not cover the current year, the command also displays a warning.
156
+
157
+ The exact local transition time depends on the operating-system timezone and daylight-saving rules. Pricing classification is based on UTC, while holiday lookup uses the Asia/Shanghai calendar date.
158
+
159
+ ## Chinese public holidays
160
+
161
+ Holiday data is stored in:
162
+
163
+ ```text
164
+ src/holidays.ts
165
+ ```
166
+
167
+ Only officially verified calendar years should be added to `COVERED_HOLIDAY_YEARS`.
168
+
169
+ For the complete annual update procedure, source requirements, timezone rules, validation checklist, and required tests, see [CALENDAR_UPDATE.md](./CALENDAR_UPDATE.md).
170
+
171
+ If the current Chinese calendar year is not covered, the plugin continues to apply the normal UTC weekday and weekend schedule but displays a coverage warning. Do not suppress the warning by adding a year without adding and reviewing the complete official calendar.
172
+
173
+ ## Tests
174
+
175
+ The test suite covers:
176
+
177
+ - the first weekday peak window;
178
+ - the off-peak gap between the two peak windows;
179
+ - the second weekday peak window;
180
+ - exact peak-window boundaries;
181
+ - weekends;
182
+ - ordinary weekday off-peak periods;
183
+ - Chinese public-holiday overrides;
184
+ - calendar-year coverage;
185
+ - next-transition calculation;
186
+ - weekend skipping when finding the next peak period;
187
+ - duration formatting;
188
+ - invalid date handling.
189
+
190
+ Run:
191
+
192
+ ```sh
193
+ bun test
194
+ ```
195
+
196
+ ## Type checking
197
+
198
+ Run:
199
+
200
+ ```sh
201
+ bun run typecheck
202
+ ```
203
+
204
+ The repository uses NodeNext module resolution. Relative TypeScript imports must therefore use the emitted JavaScript extension, for example:
205
+
206
+ ```ts
207
+ import { getPricingSnapshot } from "./pricing.js"
208
+ ```
209
+
210
+ ## License
211
+
212
+ This project is licensed under the MIT License. See [LICENSE](./LICENSE).
package/package.json CHANGED
@@ -1,6 +1,57 @@
1
1
  {
2
2
  "name": "oc-deepseek-pricing",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "OpenCode TUI plugin showing DeepSeek peak and off-peak pricing windows",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "opencode",
9
+ "opencode-plugin",
10
+ "deepseek",
11
+ "pricing",
12
+ "tui"
13
+ ],
14
+ "files": [
15
+ "src/holidays.ts",
16
+ "src/pricing.ts",
17
+ "src/tui.tsx",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "exports": {
22
+ "./tui": "./src/tui.tsx"
23
+ },
24
+ "opencode": {
25
+ "tui": "./src/tui.tsx"
26
+ },
27
+ "scripts": {
28
+ "test": "bun test",
29
+ "check": "bun run format:check && bun run lint && bun run typecheck && bun test",
30
+ "format": "biome format --write .",
31
+ "format:check": "biome format .",
32
+ "lint": "biome lint .",
33
+ "lint:fix": "biome lint --write .",
34
+ "biome:check": "biome check .",
35
+ "biome:fix": "biome check --write .",
36
+ "typecheck": "tsc --noEmit",
37
+ "pack:check": "npm pack --dry-run",
38
+ "prepublishOnly": "bun run check"
39
+ },
40
+ "devDependencies": {
41
+ "@biomejs/biome": "2.6.3",
42
+ "@opencode/plugin": "2.2.0",
43
+ "@opentui/core": "0.7.0",
44
+ "@opentui/solid": "0.7.0",
45
+ "@types/bun": "1.4.4",
46
+ "solid-js": "1.9.17",
47
+ "typescript": "5.9.3"
48
+ },
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/scicco/oc-deepseek-pricing.git"
52
+ },
53
+ "bugs": {
54
+ "url": "https://github.com/scicco/oc-deepseek-pricing/issues"
55
+ },
56
+ "homepage": "https://github.com/scicco/oc-deepseek-pricing#readme"
57
+ }
@@ -0,0 +1,56 @@
1
+ // Official public-holiday periods published by the General Office of the
2
+ // State Council of the People's Republic of China.
3
+ //
4
+ // Dates use the Asia/Shanghai calendar and ISO YYYY-MM-DD format.
5
+ // Compensatory working weekends are intentionally not listed: DeepSeek's
6
+ // pricing rule explicitly treats all Saturdays and Sundays as off-peak.
7
+ export const CHINESE_PUBLIC_HOLIDAYS = new Set<string>([
8
+ // 2026 New Year's Day: January 1-3
9
+ "2026-01-01",
10
+ "2026-01-02",
11
+ "2026-01-03",
12
+
13
+ // 2026 Spring Festival: February 15-23
14
+ "2026-02-15",
15
+ "2026-02-16",
16
+ "2026-02-17",
17
+ "2026-02-18",
18
+ "2026-02-19",
19
+ "2026-02-20",
20
+ "2026-02-21",
21
+ "2026-02-22",
22
+ "2026-02-23",
23
+
24
+ // 2026 Qingming Festival: April 4-6
25
+ "2026-04-04",
26
+ "2026-04-05",
27
+ "2026-04-06",
28
+
29
+ // 2026 Labor Day: May 1-5
30
+ "2026-05-01",
31
+ "2026-05-02",
32
+ "2026-05-03",
33
+ "2026-05-04",
34
+ "2026-05-05",
35
+
36
+ // 2026 Dragon Boat Festival: June 19-21
37
+ "2026-06-19",
38
+ "2026-06-20",
39
+ "2026-06-21",
40
+
41
+ // 2026 Mid-Autumn Festival: September 25-27
42
+ "2026-09-25",
43
+ "2026-09-26",
44
+ "2026-09-27",
45
+
46
+ // 2026 National Day: October 1-7
47
+ "2026-10-01",
48
+ "2026-10-02",
49
+ "2026-10-03",
50
+ "2026-10-04",
51
+ "2026-10-05",
52
+ "2026-10-06",
53
+ "2026-10-07",
54
+ ])
55
+
56
+ export const COVERED_HOLIDAY_YEARS = new Set<number>([2026])
package/src/pricing.ts ADDED
@@ -0,0 +1,98 @@
1
+ export type PricingState = "peak" | "off-peak"
2
+ export type PricingReason =
3
+ | "peak-window"
4
+ | "outside-peak-window"
5
+ | "weekend"
6
+ | "chinese-public-holiday"
7
+
8
+ export interface PricingSnapshot {
9
+ state: PricingState
10
+ rateMultiplier: 1 | 0.5
11
+ reason: PricingReason
12
+ now: string
13
+ nextTransition: string
14
+ nextState: PricingState
15
+ secondsUntilTransition: number
16
+ holidayCalendarCovered: boolean
17
+ chinaDate: string
18
+ }
19
+
20
+ export interface PricingOptions {
21
+ chinesePublicHolidays?: ReadonlySet<string>
22
+ coveredHolidayYears?: ReadonlySet<number>
23
+ }
24
+
25
+ const PEAK_WINDOWS = [
26
+ { startHour: 1, endHour: 4 },
27
+ { startHour: 6, endHour: 10 },
28
+ ] as const
29
+ const CHINA_DATE_FORMATTER = new Intl.DateTimeFormat("en-CA", {
30
+ timeZone: "Asia/Shanghai",
31
+ year: "numeric",
32
+ month: "2-digit",
33
+ day: "2-digit",
34
+ })
35
+
36
+ function chinaDate(date: Date): string {
37
+ return CHINA_DATE_FORMATTER.format(date)
38
+ }
39
+ function isWeekday(date: Date): boolean {
40
+ const day = date.getUTCDay()
41
+ return day >= 1 && day <= 5
42
+ }
43
+ function isInsidePeakWindow(date: Date): boolean {
44
+ const hour = date.getUTCHours() + date.getUTCMinutes() / 60 + date.getUTCSeconds() / 3600
45
+ return PEAK_WINDOWS.some(({ startHour, endHour }) => hour >= startHour && hour < endHour)
46
+ }
47
+ function classify(
48
+ date: Date,
49
+ holidays: ReadonlySet<string>,
50
+ ): { state: PricingState; reason: PricingReason } {
51
+ if (holidays.has(chinaDate(date))) return { state: "off-peak", reason: "chinese-public-holiday" }
52
+ if (!isWeekday(date)) return { state: "off-peak", reason: "weekend" }
53
+ if (isInsidePeakWindow(date)) return { state: "peak", reason: "peak-window" }
54
+ return { state: "off-peak", reason: "outside-peak-window" }
55
+ }
56
+ function nextTransition(date: Date, holidays: ReadonlySet<string>): Date {
57
+ const current = classify(date, holidays).state
58
+ const cursor = new Date(date)
59
+ cursor.setUTCMilliseconds(0)
60
+ cursor.setUTCSeconds(0)
61
+ cursor.setUTCMinutes(0)
62
+ if (cursor.getTime() <= date.getTime()) cursor.setUTCHours(cursor.getUTCHours() + 1)
63
+ for (let i = 0; i < 24 * 14; i += 1) {
64
+ if (classify(cursor, holidays).state !== current) return cursor
65
+ cursor.setUTCHours(cursor.getUTCHours() + 1)
66
+ }
67
+ throw new Error("Unable to find the next DeepSeek pricing transition within 14 days")
68
+ }
69
+ export function getPricingSnapshot(
70
+ date = new Date(),
71
+ options: PricingOptions = {},
72
+ ): PricingSnapshot {
73
+ if (Number.isNaN(date.getTime())) throw new TypeError("Invalid date")
74
+ const holidays = options.chinesePublicHolidays ?? new Set<string>()
75
+ const coveredYears = options.coveredHolidayYears ?? new Set<number>()
76
+ const current = classify(date, holidays)
77
+ const transition = nextTransition(date, holidays)
78
+ const chinaDay = chinaDate(date)
79
+ return {
80
+ state: current.state,
81
+ rateMultiplier: current.state === "peak" ? 1 : 0.5,
82
+ reason: current.reason,
83
+ now: date.toISOString(),
84
+ nextTransition: transition.toISOString(),
85
+ nextState: current.state === "peak" ? "off-peak" : "peak",
86
+ secondsUntilTransition: Math.max(0, Math.ceil((transition.getTime() - date.getTime()) / 1000)),
87
+ holidayCalendarCovered: coveredYears.has(Number(chinaDay.slice(0, 4))),
88
+ chinaDate: chinaDay,
89
+ }
90
+ }
91
+ export function formatDuration(totalSeconds: number): string {
92
+ const minutes = Math.max(0, Math.ceil(totalSeconds / 60))
93
+ const hours = Math.floor(minutes / 60)
94
+ const rest = minutes % 60
95
+ if (hours === 0) return `${rest}m`
96
+ if (rest === 0) return `${hours}h`
97
+ return `${hours}h ${rest}m`
98
+ }
package/src/tui.tsx ADDED
@@ -0,0 +1,235 @@
1
+ /** @jsxImportSource @opentui/solid */
2
+
3
+ import { Plugin } from "@opencode/plugin/tui"
4
+ import { createSignal } from "solid-js"
5
+ import { CHINESE_PUBLIC_HOLIDAYS, COVERED_HOLIDAY_YEARS } from "./holidays.js"
6
+ import {
7
+ formatDuration,
8
+ getPricingSnapshot,
9
+ type PricingReason,
10
+ type PricingSnapshot,
11
+ type PricingState,
12
+ } from "./pricing.js"
13
+
14
+ function createSnapshot(): PricingSnapshot {
15
+ return getPricingSnapshot(new Date(), {
16
+ chinesePublicHolidays: CHINESE_PUBLIC_HOLIDAYS,
17
+ coveredHolidayYears: COVERED_HOLIDAY_YEARS,
18
+ })
19
+ }
20
+
21
+ function stateLabel(state: PricingState): string {
22
+ return state === "peak" ? "🐳 peak" : "🐋💤 off-peak"
23
+ }
24
+
25
+ function stateName(state: PricingState): string {
26
+ return state === "peak" ? "peak" : "off-peak"
27
+ }
28
+
29
+ function StateLabel(props: { state: PricingState }) {
30
+ if (props.state === "peak") {
31
+ return <text>🐳 peak</text>
32
+ }
33
+
34
+ return (
35
+ <box flexDirection="row">
36
+ <text>🐋💤 </text>
37
+ <text fg="gray">off-peak</text>
38
+ </box>
39
+ )
40
+ }
41
+
42
+ function reasonLabel(reason: PricingReason): string {
43
+ switch (reason) {
44
+ case "peak-window":
45
+ return "weekday peak window"
46
+ case "outside-peak-window":
47
+ return "outside peak windows"
48
+ case "weekend":
49
+ return "weekend"
50
+ case "chinese-public-holiday":
51
+ return "Chinese public holiday"
52
+ }
53
+ }
54
+
55
+ function formatLocalTransition(snapshot: PricingSnapshot): string {
56
+ return new Date(snapshot.nextTransition).toLocaleString()
57
+ }
58
+
59
+ function isDeepSeekSelected(
60
+ model:
61
+ | {
62
+ readonly providerID: string
63
+ readonly modelID: string
64
+ readonly variant?: string
65
+ }
66
+ | undefined,
67
+ ): boolean {
68
+ return model?.modelID.toLowerCase().includes("deepseek") ?? false
69
+ }
70
+
71
+ export default Plugin.define({
72
+ id: "oc-deepseek-pricing",
73
+
74
+ setup(ctx) {
75
+ const [snapshot, setSnapshot] = createSignal(createSnapshot())
76
+
77
+ let transitionTimer: ReturnType<typeof setTimeout> | undefined
78
+ let minuteTimer: ReturnType<typeof setInterval> | undefined
79
+
80
+ const refresh = (): PricingSnapshot => {
81
+ const updated = createSnapshot()
82
+ setSnapshot(updated)
83
+ return updated
84
+ }
85
+
86
+ const scheduleTransition = (): void => {
87
+ if (transitionTimer !== undefined) {
88
+ clearTimeout(transitionTimer)
89
+ }
90
+
91
+ const current = snapshot()
92
+ const delay = Math.max(1_000, current.secondsUntilTransition * 1_000 + 250)
93
+
94
+ transitionTimer = setTimeout(() => {
95
+ refresh()
96
+ scheduleTransition()
97
+ }, delay)
98
+ }
99
+
100
+ const showStatus = (): void => {
101
+ const current = refresh()
102
+
103
+ const lines = [
104
+ `Status ${stateLabel(current.state)}`,
105
+ `Reason ${reasonLabel(current.reason)}`,
106
+ `Next ${stateName(current.nextState)} in ${formatDuration(current.secondsUntilTransition)}`,
107
+ `At ${formatLocalTransition(current)}`,
108
+ `China ${current.chinaDate}`,
109
+ ]
110
+
111
+ if (!current.holidayCalendarCovered) {
112
+ lines.push("Warning: Chinese holiday calendar is not configured for this year")
113
+ }
114
+
115
+ ctx.ui.toast.show({
116
+ title: "DeepSeek pricing",
117
+ message: lines.join("\n"),
118
+ variant: current.state === "peak" ? "warning" : "success",
119
+ })
120
+ }
121
+
122
+ const removePromptFooter = ctx.ui.slot({
123
+ append: "prompt.footer.status",
124
+
125
+ render() {
126
+ const model = ctx.ui.model.current()
127
+
128
+ if (!isDeepSeekSelected(model)) {
129
+ return null
130
+ }
131
+
132
+ const current = snapshot()
133
+
134
+ return (
135
+ <box flexDirection="row">
136
+ <StateLabel state={current.state} />
137
+ <text>
138
+ {" · "}
139
+ {formatDuration(current.secondsUntilTransition)} left
140
+ </text>
141
+ </box>
142
+ )
143
+ },
144
+ })
145
+
146
+ const removeHomeFooter = ctx.ui.slot({
147
+ append: "home.footer.status",
148
+
149
+ render() {
150
+ const model = ctx.ui.model.current()
151
+
152
+ if (!isDeepSeekSelected(model)) {
153
+ return null
154
+ }
155
+
156
+ const current = snapshot()
157
+
158
+ return (
159
+ <box flexDirection="row">
160
+ <StateLabel state={current.state} />
161
+ <text>
162
+ {" · "}
163
+ {formatDuration(current.secondsUntilTransition)} left
164
+ </text>
165
+ </box>
166
+ )
167
+ },
168
+ })
169
+
170
+ const removeSidebar = ctx.ui.slot({
171
+ append: "sidebar.content",
172
+
173
+ render() {
174
+ const model = ctx.ui.model.current()
175
+
176
+ if (!isDeepSeekSelected(model)) {
177
+ return null
178
+ }
179
+
180
+ const current = snapshot()
181
+
182
+ return (
183
+ <box flexDirection="column">
184
+ <box flexDirection="row">
185
+ <text>Status </text>
186
+ <StateLabel state={current.state} />
187
+ </box>
188
+ <text>{formatDuration(current.secondsUntilTransition)} left</text>
189
+ </box>
190
+ )
191
+ },
192
+ })
193
+
194
+ ctx.keymap.layer(() => ({
195
+ mode: "global",
196
+ commands: [
197
+ {
198
+ id: "deepseek-pricing.status",
199
+ title: "DeepSeek pricing status",
200
+ description: "Show the current DeepSeek peak or off-peak pricing window",
201
+ group: "DeepSeek",
202
+ enabled: () => isDeepSeekSelected(ctx.ui.model.current()),
203
+ palette: true,
204
+ slash: {
205
+ name: "deepseek-pricing",
206
+ aliases: ["deepseek-price"],
207
+ },
208
+ run: async () => {
209
+ showStatus()
210
+ },
211
+ },
212
+ ],
213
+ }))
214
+
215
+ minuteTimer = setInterval(() => {
216
+ refresh()
217
+ }, 60_000)
218
+
219
+ scheduleTransition()
220
+
221
+ return () => {
222
+ if (transitionTimer !== undefined) {
223
+ clearTimeout(transitionTimer)
224
+ }
225
+
226
+ if (minuteTimer !== undefined) {
227
+ clearInterval(minuteTimer)
228
+ }
229
+
230
+ removePromptFooter()
231
+ removeHomeFooter()
232
+ removeSidebar()
233
+ }
234
+ },
235
+ })