@suds-cli/timer 0.0.0 → 0.1.0-alpha.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/README.md +28 -27
- package/dist/index.cjs +154 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +81 -0
- package/dist/index.d.ts +81 -3
- package/dist/index.js +148 -2
- package/dist/index.js.map +1 -1
- package/package.json +27 -14
- package/dist/index.d.ts.map +0 -1
- package/dist/messages.d.ts +0 -37
- package/dist/messages.d.ts.map +0 -1
- package/dist/messages.js +0 -41
- package/dist/messages.js.map +0 -1
- package/dist/model.d.ts +0 -43
- package/dist/model.d.ts.map +0 -1
- package/dist/model.js +0 -129
- package/dist/model.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
> **Deprecation Notice:** This package is being renamed from `@suds-cli/timer` to `@boba-cli/timer`. Please update your dependencies accordingly.
|
|
2
|
+
|
|
1
3
|
# @suds-cli/timer
|
|
2
4
|
|
|
3
5
|
Countdown timer component for Suds terminal UIs. Port of Charmbracelet Bubbles timer.
|
|
4
6
|
|
|
7
|
+
<img src="../../examples/timer-demo.gif" width="950" alt="Timer component demo" />
|
|
8
|
+
|
|
5
9
|
## Install
|
|
6
10
|
|
|
7
11
|
```bash
|
|
@@ -11,49 +15,49 @@ pnpm add @suds-cli/timer
|
|
|
11
15
|
## Quickstart
|
|
12
16
|
|
|
13
17
|
```ts
|
|
14
|
-
import { TimerModel, TickMsg, TimeoutMsg } from
|
|
15
|
-
import type { Cmd, Msg, Model } from
|
|
18
|
+
import { TimerModel, TickMsg, TimeoutMsg } from '@suds-cli/timer'
|
|
19
|
+
import type { Cmd, Msg, Model } from '@suds-cli/tea'
|
|
16
20
|
|
|
17
|
-
const timer = TimerModel.new({ timeout: 30_000 })
|
|
21
|
+
const timer = TimerModel.new({ timeout: 30_000 }) // 30 seconds
|
|
18
22
|
|
|
19
23
|
function init(): Cmd<Msg> {
|
|
20
|
-
return timer.init()
|
|
24
|
+
return timer.init()
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
function update(msg: Msg): [Model, Cmd<Msg>] {
|
|
24
28
|
if (msg instanceof TickMsg || msg instanceof TimeoutMsg) {
|
|
25
|
-
const [nextTimer, cmd] = timer.update(msg)
|
|
26
|
-
return [{ ...model, timer: nextTimer }, cmd]
|
|
29
|
+
const [nextTimer, cmd] = timer.update(msg)
|
|
30
|
+
return [{ ...model, timer: nextTimer }, cmd]
|
|
27
31
|
}
|
|
28
|
-
return [model, null]
|
|
32
|
+
return [model, null]
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
function view(): string {
|
|
32
|
-
return `Remaining ${timer.view()}
|
|
36
|
+
return `Remaining ${timer.view()}`
|
|
33
37
|
}
|
|
34
38
|
```
|
|
35
39
|
|
|
36
40
|
## API
|
|
37
41
|
|
|
38
|
-
| Export
|
|
39
|
-
|
|
40
|
-
| `TimerModel`
|
|
41
|
-
| `TimerOptions` | Options for creating a timer
|
|
42
|
-
| `TickMsg`
|
|
43
|
-
| `TimeoutMsg`
|
|
44
|
-
| `StartStopMsg` | Message to start/stop the timer
|
|
42
|
+
| Export | Description |
|
|
43
|
+
| -------------- | ----------------------------------------- |
|
|
44
|
+
| `TimerModel` | Countdown timer model |
|
|
45
|
+
| `TimerOptions` | Options for creating a timer |
|
|
46
|
+
| `TickMsg` | Tick message carrying ID/tag/timeout flag |
|
|
47
|
+
| `TimeoutMsg` | Message emitted when timer expires |
|
|
48
|
+
| `StartStopMsg` | Message to start/stop the timer |
|
|
45
49
|
|
|
46
50
|
### TimerModel methods
|
|
47
51
|
|
|
48
|
-
| Method
|
|
49
|
-
|
|
50
|
-
| `id()`
|
|
51
|
-
| `running()`
|
|
52
|
-
| `timedOut()`
|
|
53
|
-
| `init()`
|
|
54
|
-
| `update(msg)`
|
|
55
|
-
| `view()`
|
|
56
|
-
| `start()/stop()/toggle()` | Control commands
|
|
52
|
+
| Method | Description |
|
|
53
|
+
| ------------------------- | --------------------------------------- |
|
|
54
|
+
| `id()` | Unique ID for message routing |
|
|
55
|
+
| `running()` | Whether the timer is active |
|
|
56
|
+
| `timedOut()` | Whether the timer has expired |
|
|
57
|
+
| `init()` | Start ticking on init |
|
|
58
|
+
| `update(msg)` | Handle messages, returns `[model, cmd]` |
|
|
59
|
+
| `view()` | Render remaining time |
|
|
60
|
+
| `start()/stop()/toggle()` | Control commands |
|
|
57
61
|
|
|
58
62
|
## Scripts
|
|
59
63
|
|
|
@@ -65,6 +69,3 @@ function view(): string {
|
|
|
65
69
|
## License
|
|
66
70
|
|
|
67
71
|
MIT
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var tea = require('@suds-cli/tea');
|
|
4
|
+
|
|
5
|
+
// src/messages.ts
|
|
6
|
+
var TickMsg = class {
|
|
7
|
+
constructor(id, tag, timeout) {
|
|
8
|
+
this.id = id;
|
|
9
|
+
this.tag = tag;
|
|
10
|
+
this.timeout = timeout;
|
|
11
|
+
}
|
|
12
|
+
_tag = "timer-tick";
|
|
13
|
+
};
|
|
14
|
+
var TimeoutMsg = class {
|
|
15
|
+
constructor(id) {
|
|
16
|
+
this.id = id;
|
|
17
|
+
}
|
|
18
|
+
_tag = "timer-timeout";
|
|
19
|
+
};
|
|
20
|
+
var StartStopMsg = class {
|
|
21
|
+
constructor(id, running) {
|
|
22
|
+
this.id = id;
|
|
23
|
+
this.running = running;
|
|
24
|
+
}
|
|
25
|
+
_tag = "timer-start-stop";
|
|
26
|
+
};
|
|
27
|
+
var lastId = 0;
|
|
28
|
+
function nextId() {
|
|
29
|
+
return ++lastId;
|
|
30
|
+
}
|
|
31
|
+
var TimerModel = class _TimerModel {
|
|
32
|
+
timeout;
|
|
33
|
+
interval;
|
|
34
|
+
#id;
|
|
35
|
+
#tag;
|
|
36
|
+
#running;
|
|
37
|
+
constructor(options) {
|
|
38
|
+
this.timeout = options.timeout;
|
|
39
|
+
this.interval = options.interval;
|
|
40
|
+
this.#running = options.running;
|
|
41
|
+
this.#id = options.id;
|
|
42
|
+
this.#tag = options.tag;
|
|
43
|
+
}
|
|
44
|
+
/** Create a new timer with the given options. */
|
|
45
|
+
static new(options) {
|
|
46
|
+
const interval = options.interval ?? 1e3;
|
|
47
|
+
return new _TimerModel({
|
|
48
|
+
timeout: options.timeout,
|
|
49
|
+
interval,
|
|
50
|
+
running: true,
|
|
51
|
+
id: nextId(),
|
|
52
|
+
tag: 0
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** Create a new timer with explicit timeout and interval. */
|
|
56
|
+
static withInterval(timeout, interval) {
|
|
57
|
+
return _TimerModel.new({ timeout, interval });
|
|
58
|
+
}
|
|
59
|
+
/** Unique ID for message routing. */
|
|
60
|
+
id() {
|
|
61
|
+
return this.#id;
|
|
62
|
+
}
|
|
63
|
+
/** Whether the timer is currently running (false once timed out). */
|
|
64
|
+
running() {
|
|
65
|
+
if (this.timedOut()) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return this.#running;
|
|
69
|
+
}
|
|
70
|
+
/** Whether the timer has expired. */
|
|
71
|
+
timedOut() {
|
|
72
|
+
return this.timeout <= 0;
|
|
73
|
+
}
|
|
74
|
+
/** Start ticking. */
|
|
75
|
+
init() {
|
|
76
|
+
return this.tick();
|
|
77
|
+
}
|
|
78
|
+
/** Update the timer in response to a message. */
|
|
79
|
+
update(msg) {
|
|
80
|
+
if (msg instanceof StartStopMsg) {
|
|
81
|
+
if (msg.id !== 0 && msg.id !== this.#id) {
|
|
82
|
+
return [this, null];
|
|
83
|
+
}
|
|
84
|
+
const next = new _TimerModel({
|
|
85
|
+
timeout: this.timeout,
|
|
86
|
+
interval: this.interval,
|
|
87
|
+
running: msg.running,
|
|
88
|
+
id: this.#id,
|
|
89
|
+
tag: this.#tag
|
|
90
|
+
});
|
|
91
|
+
return [next, next.tick()];
|
|
92
|
+
}
|
|
93
|
+
if (msg instanceof TickMsg) {
|
|
94
|
+
if (!this.running() || msg.id !== 0 && msg.id !== this.#id) {
|
|
95
|
+
return [this, null];
|
|
96
|
+
}
|
|
97
|
+
if (msg.tag > 0 && msg.tag !== this.#tag) {
|
|
98
|
+
return [this, null];
|
|
99
|
+
}
|
|
100
|
+
const nextTimeout = this.timeout - this.interval;
|
|
101
|
+
const nextTag = this.#tag + 1;
|
|
102
|
+
const next = new _TimerModel({
|
|
103
|
+
timeout: nextTimeout,
|
|
104
|
+
interval: this.interval,
|
|
105
|
+
running: this.#running,
|
|
106
|
+
id: this.#id,
|
|
107
|
+
tag: nextTag
|
|
108
|
+
});
|
|
109
|
+
const timeoutCmd = next.timedOut() ? tea.msg(new TimeoutMsg(this.#id)) : null;
|
|
110
|
+
const tickCmd = next.timedOut() ? null : next.tick();
|
|
111
|
+
return [next, tea.batch(tickCmd, timeoutCmd)];
|
|
112
|
+
}
|
|
113
|
+
return [this, null];
|
|
114
|
+
}
|
|
115
|
+
/** Render remaining time as a human-readable string. */
|
|
116
|
+
view() {
|
|
117
|
+
return formatDuration(Math.max(0, this.timeout));
|
|
118
|
+
}
|
|
119
|
+
/** Command to start the timer. */
|
|
120
|
+
start() {
|
|
121
|
+
return this.startStop(true);
|
|
122
|
+
}
|
|
123
|
+
/** Command to stop/pause the timer. */
|
|
124
|
+
stop() {
|
|
125
|
+
return this.startStop(false);
|
|
126
|
+
}
|
|
127
|
+
/** Command to toggle running state. */
|
|
128
|
+
toggle() {
|
|
129
|
+
return this.startStop(!this.running());
|
|
130
|
+
}
|
|
131
|
+
tick() {
|
|
132
|
+
const id = this.#id;
|
|
133
|
+
const tag = this.#tag;
|
|
134
|
+
return tea.tick(this.interval, () => new TickMsg(id, tag, this.timedOut()));
|
|
135
|
+
}
|
|
136
|
+
startStop(running) {
|
|
137
|
+
return tea.msg(new StartStopMsg(this.#id, running));
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
function formatDuration(ms) {
|
|
141
|
+
const seconds = Math.floor(ms / 1e3) % 60;
|
|
142
|
+
const minutes = Math.floor(ms / 6e4) % 60;
|
|
143
|
+
const hours = Math.floor(ms / 36e5);
|
|
144
|
+
if (hours > 0) return `${hours}h${minutes}m${seconds}s`;
|
|
145
|
+
if (minutes > 0) return `${minutes}m${seconds}s`;
|
|
146
|
+
return `${seconds}s`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
exports.StartStopMsg = StartStopMsg;
|
|
150
|
+
exports.TickMsg = TickMsg;
|
|
151
|
+
exports.TimeoutMsg = TimeoutMsg;
|
|
152
|
+
exports.TimerModel = TimerModel;
|
|
153
|
+
//# sourceMappingURL=index.cjs.map
|
|
154
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/messages.ts","../src/model.ts"],"names":["lift","batch","tick"],"mappings":"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","file":"index.cjs","sourcesContent":["/** Tick message for timer countdown. @public */\nexport class TickMsg {\n readonly _tag = 'timer-tick'\n\n constructor(\n /** Unique timer ID */\n public readonly id: number,\n /** Internal tag for deduplication */\n public readonly tag: number,\n /** Whether this tick indicates the timer expired */\n public readonly timeout: boolean,\n ) {}\n}\n\n/** Message emitted once when the timer times out. @public */\nexport class TimeoutMsg {\n readonly _tag = 'timer-timeout'\n\n constructor(public readonly id: number) {}\n}\n\n/** Message that starts or stops the timer. @public */\nexport class StartStopMsg {\n readonly _tag = 'timer-start-stop'\n\n constructor(\n /** Unique timer ID */\n public readonly id: number,\n /** True to run, false to pause */\n public readonly running: boolean,\n ) {}\n}\n","import {\n batch,\n msg as lift,\n tick,\n type Cmd,\n type Msg as TeaMsg,\n type Model as TeaModel,\n} from '@suds-cli/tea'\nimport { StartStopMsg, TickMsg, TimeoutMsg } from './messages.js'\n\n// Module-level ID counter for unique timers\nlet lastId = 0\nfunction nextId(): number {\n return ++lastId\n}\n\n/** Options for creating a timer. @public */\nexport interface TimerOptions {\n /** Milliseconds until the timer expires. */\n timeout: number\n /** Tick interval in milliseconds (default: 1000). */\n interval?: number\n}\n\n/** Timer messages. @public */\nexport type TimerMsg = TickMsg | TimeoutMsg | StartStopMsg\n\n/** Countdown timer model. @public */\nexport class TimerModel implements TeaModel<TimerMsg, TimerModel> {\n readonly timeout: number\n readonly interval: number\n readonly #id: number\n readonly #tag: number\n readonly #running: boolean\n\n private constructor(options: {\n timeout: number\n interval: number\n running: boolean\n id: number\n tag: number\n }) {\n this.timeout = options.timeout\n this.interval = options.interval\n this.#running = options.running\n this.#id = options.id\n this.#tag = options.tag\n }\n\n /** Create a new timer with the given options. */\n static new(options: TimerOptions): TimerModel {\n const interval = options.interval ?? 1000\n return new TimerModel({\n timeout: options.timeout,\n interval,\n running: true,\n id: nextId(),\n tag: 0,\n })\n }\n\n /** Create a new timer with explicit timeout and interval. */\n static withInterval(timeout: number, interval: number): TimerModel {\n return TimerModel.new({ timeout, interval })\n }\n\n /** Unique ID for message routing. */\n id(): number {\n return this.#id\n }\n\n /** Whether the timer is currently running (false once timed out). */\n running(): boolean {\n if (this.timedOut()) {\n return false\n }\n return this.#running\n }\n\n /** Whether the timer has expired. */\n timedOut(): boolean {\n return this.timeout <= 0\n }\n\n /** Start ticking. */\n init(): Cmd<TimerMsg> {\n return this.tick()\n }\n\n /** Update the timer in response to a message. */\n update(msg: TeaMsg): [TimerModel, Cmd<TimerMsg>] {\n if (msg instanceof StartStopMsg) {\n if (msg.id !== 0 && msg.id !== this.#id) {\n return [this, null]\n }\n const next = new TimerModel({\n timeout: this.timeout,\n interval: this.interval,\n running: msg.running,\n id: this.#id,\n tag: this.#tag,\n })\n return [next, next.tick()]\n }\n\n if (msg instanceof TickMsg) {\n if (!this.running() || (msg.id !== 0 && msg.id !== this.#id)) {\n return [this, null]\n }\n\n if (msg.tag > 0 && msg.tag !== this.#tag) {\n return [this, null]\n }\n\n const nextTimeout = this.timeout - this.interval\n const nextTag = this.#tag + 1\n\n const next = new TimerModel({\n timeout: nextTimeout,\n interval: this.interval,\n running: this.#running,\n id: this.#id,\n tag: nextTag,\n })\n\n const timeoutCmd = next.timedOut() ? lift(new TimeoutMsg(this.#id)) : null\n const tickCmd = next.timedOut() ? null : next.tick()\n\n return [next, batch(tickCmd, timeoutCmd)]\n }\n\n return [this, null]\n }\n\n /** Render remaining time as a human-readable string. */\n view(): string {\n return formatDuration(Math.max(0, this.timeout))\n }\n\n /** Command to start the timer. */\n start(): Cmd<TimerMsg> {\n return this.startStop(true)\n }\n\n /** Command to stop/pause the timer. */\n stop(): Cmd<TimerMsg> {\n return this.startStop(false)\n }\n\n /** Command to toggle running state. */\n toggle(): Cmd<TimerMsg> {\n return this.startStop(!this.running())\n }\n\n private tick(): Cmd<TimerMsg> {\n const id = this.#id\n const tag = this.#tag\n return tick(this.interval, () => new TickMsg(id, tag, this.timedOut()))\n }\n\n private startStop(running: boolean): Cmd<TimerMsg> {\n return lift(new StartStopMsg(this.#id, running))\n }\n}\n\n// Simple duration formatter (e.g., 1h2m3s)\nfunction formatDuration(ms: number): string {\n const seconds = Math.floor(ms / 1000) % 60\n const minutes = Math.floor(ms / 60000) % 60\n const hours = Math.floor(ms / 3600000)\n\n if (hours > 0) return `${hours}h${minutes}m${seconds}s`\n if (minutes > 0) return `${minutes}m${seconds}s`\n return `${seconds}s`\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Model, Cmd, Msg } from '@suds-cli/tea';
|
|
2
|
+
|
|
3
|
+
/** Tick message for timer countdown. @public */
|
|
4
|
+
declare class TickMsg {
|
|
5
|
+
/** Unique timer ID */
|
|
6
|
+
readonly id: number;
|
|
7
|
+
/** Internal tag for deduplication */
|
|
8
|
+
readonly tag: number;
|
|
9
|
+
/** Whether this tick indicates the timer expired */
|
|
10
|
+
readonly timeout: boolean;
|
|
11
|
+
readonly _tag = "timer-tick";
|
|
12
|
+
constructor(
|
|
13
|
+
/** Unique timer ID */
|
|
14
|
+
id: number,
|
|
15
|
+
/** Internal tag for deduplication */
|
|
16
|
+
tag: number,
|
|
17
|
+
/** Whether this tick indicates the timer expired */
|
|
18
|
+
timeout: boolean);
|
|
19
|
+
}
|
|
20
|
+
/** Message emitted once when the timer times out. @public */
|
|
21
|
+
declare class TimeoutMsg {
|
|
22
|
+
readonly id: number;
|
|
23
|
+
readonly _tag = "timer-timeout";
|
|
24
|
+
constructor(id: number);
|
|
25
|
+
}
|
|
26
|
+
/** Message that starts or stops the timer. @public */
|
|
27
|
+
declare class StartStopMsg {
|
|
28
|
+
/** Unique timer ID */
|
|
29
|
+
readonly id: number;
|
|
30
|
+
/** True to run, false to pause */
|
|
31
|
+
readonly running: boolean;
|
|
32
|
+
readonly _tag = "timer-start-stop";
|
|
33
|
+
constructor(
|
|
34
|
+
/** Unique timer ID */
|
|
35
|
+
id: number,
|
|
36
|
+
/** True to run, false to pause */
|
|
37
|
+
running: boolean);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Options for creating a timer. @public */
|
|
41
|
+
interface TimerOptions {
|
|
42
|
+
/** Milliseconds until the timer expires. */
|
|
43
|
+
timeout: number;
|
|
44
|
+
/** Tick interval in milliseconds (default: 1000). */
|
|
45
|
+
interval?: number;
|
|
46
|
+
}
|
|
47
|
+
/** Timer messages. @public */
|
|
48
|
+
type TimerMsg = TickMsg | TimeoutMsg | StartStopMsg;
|
|
49
|
+
/** Countdown timer model. @public */
|
|
50
|
+
declare class TimerModel implements Model<TimerMsg, TimerModel> {
|
|
51
|
+
#private;
|
|
52
|
+
readonly timeout: number;
|
|
53
|
+
readonly interval: number;
|
|
54
|
+
private constructor();
|
|
55
|
+
/** Create a new timer with the given options. */
|
|
56
|
+
static new(options: TimerOptions): TimerModel;
|
|
57
|
+
/** Create a new timer with explicit timeout and interval. */
|
|
58
|
+
static withInterval(timeout: number, interval: number): TimerModel;
|
|
59
|
+
/** Unique ID for message routing. */
|
|
60
|
+
id(): number;
|
|
61
|
+
/** Whether the timer is currently running (false once timed out). */
|
|
62
|
+
running(): boolean;
|
|
63
|
+
/** Whether the timer has expired. */
|
|
64
|
+
timedOut(): boolean;
|
|
65
|
+
/** Start ticking. */
|
|
66
|
+
init(): Cmd<TimerMsg>;
|
|
67
|
+
/** Update the timer in response to a message. */
|
|
68
|
+
update(msg: Msg): [TimerModel, Cmd<TimerMsg>];
|
|
69
|
+
/** Render remaining time as a human-readable string. */
|
|
70
|
+
view(): string;
|
|
71
|
+
/** Command to start the timer. */
|
|
72
|
+
start(): Cmd<TimerMsg>;
|
|
73
|
+
/** Command to stop/pause the timer. */
|
|
74
|
+
stop(): Cmd<TimerMsg>;
|
|
75
|
+
/** Command to toggle running state. */
|
|
76
|
+
toggle(): Cmd<TimerMsg>;
|
|
77
|
+
private tick;
|
|
78
|
+
private startStop;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { StartStopMsg, TickMsg, TimeoutMsg, TimerModel, type TimerMsg, type TimerOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,81 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Model, Cmd, Msg } from '@suds-cli/tea';
|
|
2
|
+
|
|
3
|
+
/** Tick message for timer countdown. @public */
|
|
4
|
+
declare class TickMsg {
|
|
5
|
+
/** Unique timer ID */
|
|
6
|
+
readonly id: number;
|
|
7
|
+
/** Internal tag for deduplication */
|
|
8
|
+
readonly tag: number;
|
|
9
|
+
/** Whether this tick indicates the timer expired */
|
|
10
|
+
readonly timeout: boolean;
|
|
11
|
+
readonly _tag = "timer-tick";
|
|
12
|
+
constructor(
|
|
13
|
+
/** Unique timer ID */
|
|
14
|
+
id: number,
|
|
15
|
+
/** Internal tag for deduplication */
|
|
16
|
+
tag: number,
|
|
17
|
+
/** Whether this tick indicates the timer expired */
|
|
18
|
+
timeout: boolean);
|
|
19
|
+
}
|
|
20
|
+
/** Message emitted once when the timer times out. @public */
|
|
21
|
+
declare class TimeoutMsg {
|
|
22
|
+
readonly id: number;
|
|
23
|
+
readonly _tag = "timer-timeout";
|
|
24
|
+
constructor(id: number);
|
|
25
|
+
}
|
|
26
|
+
/** Message that starts or stops the timer. @public */
|
|
27
|
+
declare class StartStopMsg {
|
|
28
|
+
/** Unique timer ID */
|
|
29
|
+
readonly id: number;
|
|
30
|
+
/** True to run, false to pause */
|
|
31
|
+
readonly running: boolean;
|
|
32
|
+
readonly _tag = "timer-start-stop";
|
|
33
|
+
constructor(
|
|
34
|
+
/** Unique timer ID */
|
|
35
|
+
id: number,
|
|
36
|
+
/** True to run, false to pause */
|
|
37
|
+
running: boolean);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Options for creating a timer. @public */
|
|
41
|
+
interface TimerOptions {
|
|
42
|
+
/** Milliseconds until the timer expires. */
|
|
43
|
+
timeout: number;
|
|
44
|
+
/** Tick interval in milliseconds (default: 1000). */
|
|
45
|
+
interval?: number;
|
|
46
|
+
}
|
|
47
|
+
/** Timer messages. @public */
|
|
48
|
+
type TimerMsg = TickMsg | TimeoutMsg | StartStopMsg;
|
|
49
|
+
/** Countdown timer model. @public */
|
|
50
|
+
declare class TimerModel implements Model<TimerMsg, TimerModel> {
|
|
51
|
+
#private;
|
|
52
|
+
readonly timeout: number;
|
|
53
|
+
readonly interval: number;
|
|
54
|
+
private constructor();
|
|
55
|
+
/** Create a new timer with the given options. */
|
|
56
|
+
static new(options: TimerOptions): TimerModel;
|
|
57
|
+
/** Create a new timer with explicit timeout and interval. */
|
|
58
|
+
static withInterval(timeout: number, interval: number): TimerModel;
|
|
59
|
+
/** Unique ID for message routing. */
|
|
60
|
+
id(): number;
|
|
61
|
+
/** Whether the timer is currently running (false once timed out). */
|
|
62
|
+
running(): boolean;
|
|
63
|
+
/** Whether the timer has expired. */
|
|
64
|
+
timedOut(): boolean;
|
|
65
|
+
/** Start ticking. */
|
|
66
|
+
init(): Cmd<TimerMsg>;
|
|
67
|
+
/** Update the timer in response to a message. */
|
|
68
|
+
update(msg: Msg): [TimerModel, Cmd<TimerMsg>];
|
|
69
|
+
/** Render remaining time as a human-readable string. */
|
|
70
|
+
view(): string;
|
|
71
|
+
/** Command to start the timer. */
|
|
72
|
+
start(): Cmd<TimerMsg>;
|
|
73
|
+
/** Command to stop/pause the timer. */
|
|
74
|
+
stop(): Cmd<TimerMsg>;
|
|
75
|
+
/** Command to toggle running state. */
|
|
76
|
+
toggle(): Cmd<TimerMsg>;
|
|
77
|
+
private tick;
|
|
78
|
+
private startStop;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { StartStopMsg, TickMsg, TimeoutMsg, TimerModel, type TimerMsg, type TimerOptions };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,149 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { msg, batch, tick } from '@suds-cli/tea';
|
|
2
|
+
|
|
3
|
+
// src/messages.ts
|
|
4
|
+
var TickMsg = class {
|
|
5
|
+
constructor(id, tag, timeout) {
|
|
6
|
+
this.id = id;
|
|
7
|
+
this.tag = tag;
|
|
8
|
+
this.timeout = timeout;
|
|
9
|
+
}
|
|
10
|
+
_tag = "timer-tick";
|
|
11
|
+
};
|
|
12
|
+
var TimeoutMsg = class {
|
|
13
|
+
constructor(id) {
|
|
14
|
+
this.id = id;
|
|
15
|
+
}
|
|
16
|
+
_tag = "timer-timeout";
|
|
17
|
+
};
|
|
18
|
+
var StartStopMsg = class {
|
|
19
|
+
constructor(id, running) {
|
|
20
|
+
this.id = id;
|
|
21
|
+
this.running = running;
|
|
22
|
+
}
|
|
23
|
+
_tag = "timer-start-stop";
|
|
24
|
+
};
|
|
25
|
+
var lastId = 0;
|
|
26
|
+
function nextId() {
|
|
27
|
+
return ++lastId;
|
|
28
|
+
}
|
|
29
|
+
var TimerModel = class _TimerModel {
|
|
30
|
+
timeout;
|
|
31
|
+
interval;
|
|
32
|
+
#id;
|
|
33
|
+
#tag;
|
|
34
|
+
#running;
|
|
35
|
+
constructor(options) {
|
|
36
|
+
this.timeout = options.timeout;
|
|
37
|
+
this.interval = options.interval;
|
|
38
|
+
this.#running = options.running;
|
|
39
|
+
this.#id = options.id;
|
|
40
|
+
this.#tag = options.tag;
|
|
41
|
+
}
|
|
42
|
+
/** Create a new timer with the given options. */
|
|
43
|
+
static new(options) {
|
|
44
|
+
const interval = options.interval ?? 1e3;
|
|
45
|
+
return new _TimerModel({
|
|
46
|
+
timeout: options.timeout,
|
|
47
|
+
interval,
|
|
48
|
+
running: true,
|
|
49
|
+
id: nextId(),
|
|
50
|
+
tag: 0
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** Create a new timer with explicit timeout and interval. */
|
|
54
|
+
static withInterval(timeout, interval) {
|
|
55
|
+
return _TimerModel.new({ timeout, interval });
|
|
56
|
+
}
|
|
57
|
+
/** Unique ID for message routing. */
|
|
58
|
+
id() {
|
|
59
|
+
return this.#id;
|
|
60
|
+
}
|
|
61
|
+
/** Whether the timer is currently running (false once timed out). */
|
|
62
|
+
running() {
|
|
63
|
+
if (this.timedOut()) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return this.#running;
|
|
67
|
+
}
|
|
68
|
+
/** Whether the timer has expired. */
|
|
69
|
+
timedOut() {
|
|
70
|
+
return this.timeout <= 0;
|
|
71
|
+
}
|
|
72
|
+
/** Start ticking. */
|
|
73
|
+
init() {
|
|
74
|
+
return this.tick();
|
|
75
|
+
}
|
|
76
|
+
/** Update the timer in response to a message. */
|
|
77
|
+
update(msg$1) {
|
|
78
|
+
if (msg$1 instanceof StartStopMsg) {
|
|
79
|
+
if (msg$1.id !== 0 && msg$1.id !== this.#id) {
|
|
80
|
+
return [this, null];
|
|
81
|
+
}
|
|
82
|
+
const next = new _TimerModel({
|
|
83
|
+
timeout: this.timeout,
|
|
84
|
+
interval: this.interval,
|
|
85
|
+
running: msg$1.running,
|
|
86
|
+
id: this.#id,
|
|
87
|
+
tag: this.#tag
|
|
88
|
+
});
|
|
89
|
+
return [next, next.tick()];
|
|
90
|
+
}
|
|
91
|
+
if (msg$1 instanceof TickMsg) {
|
|
92
|
+
if (!this.running() || msg$1.id !== 0 && msg$1.id !== this.#id) {
|
|
93
|
+
return [this, null];
|
|
94
|
+
}
|
|
95
|
+
if (msg$1.tag > 0 && msg$1.tag !== this.#tag) {
|
|
96
|
+
return [this, null];
|
|
97
|
+
}
|
|
98
|
+
const nextTimeout = this.timeout - this.interval;
|
|
99
|
+
const nextTag = this.#tag + 1;
|
|
100
|
+
const next = new _TimerModel({
|
|
101
|
+
timeout: nextTimeout,
|
|
102
|
+
interval: this.interval,
|
|
103
|
+
running: this.#running,
|
|
104
|
+
id: this.#id,
|
|
105
|
+
tag: nextTag
|
|
106
|
+
});
|
|
107
|
+
const timeoutCmd = next.timedOut() ? msg(new TimeoutMsg(this.#id)) : null;
|
|
108
|
+
const tickCmd = next.timedOut() ? null : next.tick();
|
|
109
|
+
return [next, batch(tickCmd, timeoutCmd)];
|
|
110
|
+
}
|
|
111
|
+
return [this, null];
|
|
112
|
+
}
|
|
113
|
+
/** Render remaining time as a human-readable string. */
|
|
114
|
+
view() {
|
|
115
|
+
return formatDuration(Math.max(0, this.timeout));
|
|
116
|
+
}
|
|
117
|
+
/** Command to start the timer. */
|
|
118
|
+
start() {
|
|
119
|
+
return this.startStop(true);
|
|
120
|
+
}
|
|
121
|
+
/** Command to stop/pause the timer. */
|
|
122
|
+
stop() {
|
|
123
|
+
return this.startStop(false);
|
|
124
|
+
}
|
|
125
|
+
/** Command to toggle running state. */
|
|
126
|
+
toggle() {
|
|
127
|
+
return this.startStop(!this.running());
|
|
128
|
+
}
|
|
129
|
+
tick() {
|
|
130
|
+
const id = this.#id;
|
|
131
|
+
const tag = this.#tag;
|
|
132
|
+
return tick(this.interval, () => new TickMsg(id, tag, this.timedOut()));
|
|
133
|
+
}
|
|
134
|
+
startStop(running) {
|
|
135
|
+
return msg(new StartStopMsg(this.#id, running));
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
function formatDuration(ms) {
|
|
139
|
+
const seconds = Math.floor(ms / 1e3) % 60;
|
|
140
|
+
const minutes = Math.floor(ms / 6e4) % 60;
|
|
141
|
+
const hours = Math.floor(ms / 36e5);
|
|
142
|
+
if (hours > 0) return `${hours}h${minutes}m${seconds}s`;
|
|
143
|
+
if (minutes > 0) return `${minutes}m${seconds}s`;
|
|
144
|
+
return `${seconds}s`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export { StartStopMsg, TickMsg, TimeoutMsg, TimerModel };
|
|
148
|
+
//# sourceMappingURL=index.js.map
|
|
3
149
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/messages.ts","../src/model.ts"],"names":["msg","lift"],"mappings":"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","file":"index.js","sourcesContent":["/** Tick message for timer countdown. @public */\nexport class TickMsg {\n readonly _tag = 'timer-tick'\n\n constructor(\n /** Unique timer ID */\n public readonly id: number,\n /** Internal tag for deduplication */\n public readonly tag: number,\n /** Whether this tick indicates the timer expired */\n public readonly timeout: boolean,\n ) {}\n}\n\n/** Message emitted once when the timer times out. @public */\nexport class TimeoutMsg {\n readonly _tag = 'timer-timeout'\n\n constructor(public readonly id: number) {}\n}\n\n/** Message that starts or stops the timer. @public */\nexport class StartStopMsg {\n readonly _tag = 'timer-start-stop'\n\n constructor(\n /** Unique timer ID */\n public readonly id: number,\n /** True to run, false to pause */\n public readonly running: boolean,\n ) {}\n}\n","import {\n batch,\n msg as lift,\n tick,\n type Cmd,\n type Msg as TeaMsg,\n type Model as TeaModel,\n} from '@suds-cli/tea'\nimport { StartStopMsg, TickMsg, TimeoutMsg } from './messages.js'\n\n// Module-level ID counter for unique timers\nlet lastId = 0\nfunction nextId(): number {\n return ++lastId\n}\n\n/** Options for creating a timer. @public */\nexport interface TimerOptions {\n /** Milliseconds until the timer expires. */\n timeout: number\n /** Tick interval in milliseconds (default: 1000). */\n interval?: number\n}\n\n/** Timer messages. @public */\nexport type TimerMsg = TickMsg | TimeoutMsg | StartStopMsg\n\n/** Countdown timer model. @public */\nexport class TimerModel implements TeaModel<TimerMsg, TimerModel> {\n readonly timeout: number\n readonly interval: number\n readonly #id: number\n readonly #tag: number\n readonly #running: boolean\n\n private constructor(options: {\n timeout: number\n interval: number\n running: boolean\n id: number\n tag: number\n }) {\n this.timeout = options.timeout\n this.interval = options.interval\n this.#running = options.running\n this.#id = options.id\n this.#tag = options.tag\n }\n\n /** Create a new timer with the given options. */\n static new(options: TimerOptions): TimerModel {\n const interval = options.interval ?? 1000\n return new TimerModel({\n timeout: options.timeout,\n interval,\n running: true,\n id: nextId(),\n tag: 0,\n })\n }\n\n /** Create a new timer with explicit timeout and interval. */\n static withInterval(timeout: number, interval: number): TimerModel {\n return TimerModel.new({ timeout, interval })\n }\n\n /** Unique ID for message routing. */\n id(): number {\n return this.#id\n }\n\n /** Whether the timer is currently running (false once timed out). */\n running(): boolean {\n if (this.timedOut()) {\n return false\n }\n return this.#running\n }\n\n /** Whether the timer has expired. */\n timedOut(): boolean {\n return this.timeout <= 0\n }\n\n /** Start ticking. */\n init(): Cmd<TimerMsg> {\n return this.tick()\n }\n\n /** Update the timer in response to a message. */\n update(msg: TeaMsg): [TimerModel, Cmd<TimerMsg>] {\n if (msg instanceof StartStopMsg) {\n if (msg.id !== 0 && msg.id !== this.#id) {\n return [this, null]\n }\n const next = new TimerModel({\n timeout: this.timeout,\n interval: this.interval,\n running: msg.running,\n id: this.#id,\n tag: this.#tag,\n })\n return [next, next.tick()]\n }\n\n if (msg instanceof TickMsg) {\n if (!this.running() || (msg.id !== 0 && msg.id !== this.#id)) {\n return [this, null]\n }\n\n if (msg.tag > 0 && msg.tag !== this.#tag) {\n return [this, null]\n }\n\n const nextTimeout = this.timeout - this.interval\n const nextTag = this.#tag + 1\n\n const next = new TimerModel({\n timeout: nextTimeout,\n interval: this.interval,\n running: this.#running,\n id: this.#id,\n tag: nextTag,\n })\n\n const timeoutCmd = next.timedOut() ? lift(new TimeoutMsg(this.#id)) : null\n const tickCmd = next.timedOut() ? null : next.tick()\n\n return [next, batch(tickCmd, timeoutCmd)]\n }\n\n return [this, null]\n }\n\n /** Render remaining time as a human-readable string. */\n view(): string {\n return formatDuration(Math.max(0, this.timeout))\n }\n\n /** Command to start the timer. */\n start(): Cmd<TimerMsg> {\n return this.startStop(true)\n }\n\n /** Command to stop/pause the timer. */\n stop(): Cmd<TimerMsg> {\n return this.startStop(false)\n }\n\n /** Command to toggle running state. */\n toggle(): Cmd<TimerMsg> {\n return this.startStop(!this.running())\n }\n\n private tick(): Cmd<TimerMsg> {\n const id = this.#id\n const tag = this.#tag\n return tick(this.interval, () => new TickMsg(id, tag, this.timedOut()))\n }\n\n private startStop(running: boolean): Cmd<TimerMsg> {\n return lift(new StartStopMsg(this.#id, running))\n }\n}\n\n// Simple duration formatter (e.g., 1h2m3s)\nfunction formatDuration(ms: number): string {\n const seconds = Math.floor(ms / 1000) % 60\n const minutes = Math.floor(ms / 60000) % 60\n const hours = Math.floor(ms / 3600000)\n\n if (hours > 0) return `${hours}h${minutes}m${seconds}s`\n if (minutes > 0) return `${minutes}m${seconds}s`\n return `${seconds}s`\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,30 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suds-cli/timer",
|
|
3
|
-
"version": "0.0.0",
|
|
4
3
|
"description": "Countdown timer component for Suds terminal UIs",
|
|
5
|
-
"
|
|
6
|
-
"main": "./dist/index.js",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"files": [
|
|
9
|
-
"dist"
|
|
10
|
-
],
|
|
4
|
+
"version": "0.1.0-alpha.1",
|
|
11
5
|
"dependencies": {
|
|
12
|
-
"@suds-cli/tea": "0.0.
|
|
6
|
+
"@suds-cli/tea": "0.1.0-alpha.1"
|
|
13
7
|
},
|
|
14
8
|
"devDependencies": {
|
|
15
9
|
"typescript": "5.8.2",
|
|
16
|
-
"vitest": "^4.0.
|
|
10
|
+
"vitest": "^4.0.16"
|
|
17
11
|
},
|
|
18
12
|
"engines": {
|
|
19
13
|
"node": ">=20.0.0"
|
|
20
14
|
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"require": {
|
|
22
|
+
"types": "./dist/index.d.cts",
|
|
23
|
+
"default": "./dist/index.cjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"module": "./dist/index.js",
|
|
33
|
+
"type": "module",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
21
35
|
"scripts": {
|
|
22
|
-
"
|
|
23
|
-
"build": "pnpm run clean && tsc -p ./tsconfig.json",
|
|
24
|
-
"test": "vitest run",
|
|
25
|
-
"generate:api-report": "api-extractor run --local",
|
|
36
|
+
"build": "tsup",
|
|
26
37
|
"check:api-report": "pnpm run generate:api-report",
|
|
27
38
|
"check:eslint": "pnpm run lint",
|
|
28
|
-
"
|
|
39
|
+
"generate:api-report": "api-extractor run --local",
|
|
40
|
+
"lint": "eslint \"{src,test}/**/*.{ts,tsx}\"",
|
|
41
|
+
"test": "vitest run"
|
|
29
42
|
}
|
|
30
43
|
}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/messages.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/** Tick message for timer countdown. @public */
|
|
2
|
-
export declare class TickMsg {
|
|
3
|
-
/** Unique timer ID */
|
|
4
|
-
readonly id: number;
|
|
5
|
-
/** Internal tag for deduplication */
|
|
6
|
-
readonly tag: number;
|
|
7
|
-
/** Whether this tick indicates the timer expired */
|
|
8
|
-
readonly timeout: boolean;
|
|
9
|
-
readonly _tag = "timer-tick";
|
|
10
|
-
constructor(
|
|
11
|
-
/** Unique timer ID */
|
|
12
|
-
id: number,
|
|
13
|
-
/** Internal tag for deduplication */
|
|
14
|
-
tag: number,
|
|
15
|
-
/** Whether this tick indicates the timer expired */
|
|
16
|
-
timeout: boolean);
|
|
17
|
-
}
|
|
18
|
-
/** Message emitted once when the timer times out. @public */
|
|
19
|
-
export declare class TimeoutMsg {
|
|
20
|
-
readonly id: number;
|
|
21
|
-
readonly _tag = "timer-timeout";
|
|
22
|
-
constructor(id: number);
|
|
23
|
-
}
|
|
24
|
-
/** Message that starts or stops the timer. @public */
|
|
25
|
-
export declare class StartStopMsg {
|
|
26
|
-
/** Unique timer ID */
|
|
27
|
-
readonly id: number;
|
|
28
|
-
/** True to run, false to pause */
|
|
29
|
-
readonly running: boolean;
|
|
30
|
-
readonly _tag = "timer-start-stop";
|
|
31
|
-
constructor(
|
|
32
|
-
/** Unique timer ID */
|
|
33
|
-
id: number,
|
|
34
|
-
/** True to run, false to pause */
|
|
35
|
-
running: boolean);
|
|
36
|
-
}
|
|
37
|
-
//# sourceMappingURL=messages.d.ts.map
|
package/dist/messages.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,qBAAa,OAAO;IAIhB,sBAAsB;aACN,EAAE,EAAE,MAAM;IAC1B,qCAAqC;aACrB,GAAG,EAAE,MAAM;IAC3B,oDAAoD;aACpC,OAAO,EAAE,OAAO;IARlC,QAAQ,CAAC,IAAI,gBAAgB;;IAG3B,sBAAsB;IACN,EAAE,EAAE,MAAM;IAC1B,qCAAqC;IACrB,GAAG,EAAE,MAAM;IAC3B,oDAAoD;IACpC,OAAO,EAAE,OAAO;CAEnC;AAED,6DAA6D;AAC7D,qBAAa,UAAU;aAGO,EAAE,EAAE,MAAM;IAFtC,QAAQ,CAAC,IAAI,mBAAmB;gBAEJ,EAAE,EAAE,MAAM;CACvC;AAED,sDAAsD;AACtD,qBAAa,YAAY;IAIrB,sBAAsB;aACN,EAAE,EAAE,MAAM;IAC1B,kCAAkC;aAClB,OAAO,EAAE,OAAO;IANlC,QAAQ,CAAC,IAAI,sBAAsB;;IAGjC,sBAAsB;IACN,EAAE,EAAE,MAAM;IAC1B,kCAAkC;IAClB,OAAO,EAAE,OAAO;CAEnC"}
|
package/dist/messages.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/** Tick message for timer countdown. @public */
|
|
2
|
-
export class TickMsg {
|
|
3
|
-
id;
|
|
4
|
-
tag;
|
|
5
|
-
timeout;
|
|
6
|
-
_tag = "timer-tick";
|
|
7
|
-
constructor(
|
|
8
|
-
/** Unique timer ID */
|
|
9
|
-
id,
|
|
10
|
-
/** Internal tag for deduplication */
|
|
11
|
-
tag,
|
|
12
|
-
/** Whether this tick indicates the timer expired */
|
|
13
|
-
timeout) {
|
|
14
|
-
this.id = id;
|
|
15
|
-
this.tag = tag;
|
|
16
|
-
this.timeout = timeout;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
/** Message emitted once when the timer times out. @public */
|
|
20
|
-
export class TimeoutMsg {
|
|
21
|
-
id;
|
|
22
|
-
_tag = "timer-timeout";
|
|
23
|
-
constructor(id) {
|
|
24
|
-
this.id = id;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/** Message that starts or stops the timer. @public */
|
|
28
|
-
export class StartStopMsg {
|
|
29
|
-
id;
|
|
30
|
-
running;
|
|
31
|
-
_tag = "timer-start-stop";
|
|
32
|
-
constructor(
|
|
33
|
-
/** Unique timer ID */
|
|
34
|
-
id,
|
|
35
|
-
/** True to run, false to pause */
|
|
36
|
-
running) {
|
|
37
|
-
this.id = id;
|
|
38
|
-
this.running = running;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=messages.js.map
|
package/dist/messages.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,MAAM,OAAO,OAAO;IAKA;IAEA;IAEA;IART,IAAI,GAAG,YAAY,CAAC;IAE7B;IACE,sBAAsB;IACN,EAAU;IAC1B,qCAAqC;IACrB,GAAW;IAC3B,oDAAoD;IACpC,OAAgB;QAJhB,OAAE,GAAF,EAAE,CAAQ;QAEV,QAAG,GAAH,GAAG,CAAQ;QAEX,YAAO,GAAP,OAAO,CAAS;IAC/B,CAAC;CACL;AAED,6DAA6D;AAC7D,MAAM,OAAO,UAAU;IAGO;IAFnB,IAAI,GAAG,eAAe,CAAC;IAEhC,YAA4B,EAAU;QAAV,OAAE,GAAF,EAAE,CAAQ;IAAG,CAAC;CAC3C;AAED,sDAAsD;AACtD,MAAM,OAAO,YAAY;IAKL;IAEA;IANT,IAAI,GAAG,kBAAkB,CAAC;IAEnC;IACE,sBAAsB;IACN,EAAU;IAC1B,kCAAkC;IAClB,OAAgB;QAFhB,OAAE,GAAF,EAAE,CAAQ;QAEV,YAAO,GAAP,OAAO,CAAS;IAC/B,CAAC;CACL"}
|
package/dist/model.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { type Cmd, type Msg as TeaMsg, type Model as TeaModel } from "@suds-cli/tea";
|
|
2
|
-
import { StartStopMsg, TickMsg, TimeoutMsg } from "./messages.js";
|
|
3
|
-
/** Options for creating a timer. @public */
|
|
4
|
-
export interface TimerOptions {
|
|
5
|
-
/** Milliseconds until the timer expires. */
|
|
6
|
-
timeout: number;
|
|
7
|
-
/** Tick interval in milliseconds (default: 1000). */
|
|
8
|
-
interval?: number;
|
|
9
|
-
}
|
|
10
|
-
/** Timer messages. @public */
|
|
11
|
-
export type TimerMsg = TickMsg | TimeoutMsg | StartStopMsg;
|
|
12
|
-
/** Countdown timer model. @public */
|
|
13
|
-
export declare class TimerModel implements TeaModel<TimerMsg, TimerModel> {
|
|
14
|
-
#private;
|
|
15
|
-
readonly timeout: number;
|
|
16
|
-
readonly interval: number;
|
|
17
|
-
private constructor();
|
|
18
|
-
/** Create a new timer with the given options. */
|
|
19
|
-
static new(options: TimerOptions): TimerModel;
|
|
20
|
-
/** Create a new timer with explicit timeout and interval. */
|
|
21
|
-
static withInterval(timeout: number, interval: number): TimerModel;
|
|
22
|
-
/** Unique ID for message routing. */
|
|
23
|
-
id(): number;
|
|
24
|
-
/** Whether the timer is currently running (false once timed out). */
|
|
25
|
-
running(): boolean;
|
|
26
|
-
/** Whether the timer has expired. */
|
|
27
|
-
timedOut(): boolean;
|
|
28
|
-
/** Start ticking. */
|
|
29
|
-
init(): Cmd<TimerMsg>;
|
|
30
|
-
/** Update the timer in response to a message. */
|
|
31
|
-
update(msg: TeaMsg): [TimerModel, Cmd<TimerMsg>];
|
|
32
|
-
/** Render remaining time as a human-readable string. */
|
|
33
|
-
view(): string;
|
|
34
|
-
/** Command to start the timer. */
|
|
35
|
-
start(): Cmd<TimerMsg>;
|
|
36
|
-
/** Command to stop/pause the timer. */
|
|
37
|
-
stop(): Cmd<TimerMsg>;
|
|
38
|
-
/** Command to toggle running state. */
|
|
39
|
-
toggle(): Cmd<TimerMsg>;
|
|
40
|
-
private tick;
|
|
41
|
-
private startStop;
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=model.d.ts.map
|
package/dist/model.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,GAAG,EAAE,KAAK,GAAG,IAAI,MAAM,EAAE,KAAK,KAAK,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC/G,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAQlE,4CAA4C;AAC5C,MAAM,WAAW,YAAY;IAC3B,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,8BAA8B;AAC9B,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,YAAY,CAAC;AAE3D,qCAAqC;AACrC,qBAAa,UAAW,YAAW,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAK1B,OAAO;IAcP,iDAAiD;IACjD,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,UAAU;IAW7C,6DAA6D;IAC7D,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU;IAIlE,qCAAqC;IACrC,EAAE,IAAI,MAAM;IAIZ,qEAAqE;IACrE,OAAO,IAAI,OAAO;IAOlB,qCAAqC;IACrC,QAAQ,IAAI,OAAO;IAInB,qBAAqB;IACrB,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC;IAIrB,iDAAiD;IACjD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;IA4ChD,wDAAwD;IACxD,IAAI,IAAI,MAAM;IAId,kCAAkC;IAClC,KAAK,IAAI,GAAG,CAAC,QAAQ,CAAC;IAItB,uCAAuC;IACvC,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC;IAIrB,uCAAuC;IACvC,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC;IAIvB,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,SAAS;CAGlB"}
|
package/dist/model.js
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { batch, msg as lift, tick } from "@suds-cli/tea";
|
|
2
|
-
import { StartStopMsg, TickMsg, TimeoutMsg } from "./messages.js";
|
|
3
|
-
// Module-level ID counter for unique timers
|
|
4
|
-
let lastId = 0;
|
|
5
|
-
function nextId() {
|
|
6
|
-
return ++lastId;
|
|
7
|
-
}
|
|
8
|
-
/** Countdown timer model. @public */
|
|
9
|
-
export class TimerModel {
|
|
10
|
-
timeout;
|
|
11
|
-
interval;
|
|
12
|
-
#id;
|
|
13
|
-
#tag;
|
|
14
|
-
#running;
|
|
15
|
-
constructor(options) {
|
|
16
|
-
this.timeout = options.timeout;
|
|
17
|
-
this.interval = options.interval;
|
|
18
|
-
this.#running = options.running;
|
|
19
|
-
this.#id = options.id;
|
|
20
|
-
this.#tag = options.tag;
|
|
21
|
-
}
|
|
22
|
-
/** Create a new timer with the given options. */
|
|
23
|
-
static new(options) {
|
|
24
|
-
const interval = options.interval ?? 1000;
|
|
25
|
-
return new TimerModel({
|
|
26
|
-
timeout: options.timeout,
|
|
27
|
-
interval,
|
|
28
|
-
running: true,
|
|
29
|
-
id: nextId(),
|
|
30
|
-
tag: 0,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
/** Create a new timer with explicit timeout and interval. */
|
|
34
|
-
static withInterval(timeout, interval) {
|
|
35
|
-
return TimerModel.new({ timeout, interval });
|
|
36
|
-
}
|
|
37
|
-
/** Unique ID for message routing. */
|
|
38
|
-
id() {
|
|
39
|
-
return this.#id;
|
|
40
|
-
}
|
|
41
|
-
/** Whether the timer is currently running (false once timed out). */
|
|
42
|
-
running() {
|
|
43
|
-
if (this.timedOut()) {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
return this.#running;
|
|
47
|
-
}
|
|
48
|
-
/** Whether the timer has expired. */
|
|
49
|
-
timedOut() {
|
|
50
|
-
return this.timeout <= 0;
|
|
51
|
-
}
|
|
52
|
-
/** Start ticking. */
|
|
53
|
-
init() {
|
|
54
|
-
return this.tick();
|
|
55
|
-
}
|
|
56
|
-
/** Update the timer in response to a message. */
|
|
57
|
-
update(msg) {
|
|
58
|
-
if (msg instanceof StartStopMsg) {
|
|
59
|
-
if (msg.id !== 0 && msg.id !== this.#id) {
|
|
60
|
-
return [this, null];
|
|
61
|
-
}
|
|
62
|
-
const next = new TimerModel({
|
|
63
|
-
timeout: this.timeout,
|
|
64
|
-
interval: this.interval,
|
|
65
|
-
running: msg.running,
|
|
66
|
-
id: this.#id,
|
|
67
|
-
tag: this.#tag,
|
|
68
|
-
});
|
|
69
|
-
return [next, next.tick()];
|
|
70
|
-
}
|
|
71
|
-
if (msg instanceof TickMsg) {
|
|
72
|
-
if (!this.running() || (msg.id !== 0 && msg.id !== this.#id)) {
|
|
73
|
-
return [this, null];
|
|
74
|
-
}
|
|
75
|
-
if (msg.tag > 0 && msg.tag !== this.#tag) {
|
|
76
|
-
return [this, null];
|
|
77
|
-
}
|
|
78
|
-
const nextTimeout = this.timeout - this.interval;
|
|
79
|
-
const nextTag = this.#tag + 1;
|
|
80
|
-
const next = new TimerModel({
|
|
81
|
-
timeout: nextTimeout,
|
|
82
|
-
interval: this.interval,
|
|
83
|
-
running: this.#running,
|
|
84
|
-
id: this.#id,
|
|
85
|
-
tag: nextTag,
|
|
86
|
-
});
|
|
87
|
-
const timeoutCmd = next.timedOut() ? lift(new TimeoutMsg(this.#id)) : null;
|
|
88
|
-
const tickCmd = next.timedOut() ? null : next.tick();
|
|
89
|
-
return [next, batch(tickCmd, timeoutCmd)];
|
|
90
|
-
}
|
|
91
|
-
return [this, null];
|
|
92
|
-
}
|
|
93
|
-
/** Render remaining time as a human-readable string. */
|
|
94
|
-
view() {
|
|
95
|
-
return formatDuration(Math.max(0, this.timeout));
|
|
96
|
-
}
|
|
97
|
-
/** Command to start the timer. */
|
|
98
|
-
start() {
|
|
99
|
-
return this.startStop(true);
|
|
100
|
-
}
|
|
101
|
-
/** Command to stop/pause the timer. */
|
|
102
|
-
stop() {
|
|
103
|
-
return this.startStop(false);
|
|
104
|
-
}
|
|
105
|
-
/** Command to toggle running state. */
|
|
106
|
-
toggle() {
|
|
107
|
-
return this.startStop(!this.running());
|
|
108
|
-
}
|
|
109
|
-
tick() {
|
|
110
|
-
const id = this.#id;
|
|
111
|
-
const tag = this.#tag;
|
|
112
|
-
return tick(this.interval, () => new TickMsg(id, tag, this.timedOut()));
|
|
113
|
-
}
|
|
114
|
-
startStop(running) {
|
|
115
|
-
return lift(new StartStopMsg(this.#id, running));
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// Simple duration formatter (e.g., 1h2m3s)
|
|
119
|
-
function formatDuration(ms) {
|
|
120
|
-
const seconds = Math.floor(ms / 1000) % 60;
|
|
121
|
-
const minutes = Math.floor(ms / 60000) % 60;
|
|
122
|
-
const hours = Math.floor(ms / 3600000);
|
|
123
|
-
if (hours > 0)
|
|
124
|
-
return `${hours}h${minutes}m${seconds}s`;
|
|
125
|
-
if (minutes > 0)
|
|
126
|
-
return `${minutes}m${seconds}s`;
|
|
127
|
-
return `${seconds}s`;
|
|
128
|
-
}
|
|
129
|
-
//# sourceMappingURL=model.js.map
|
package/dist/model.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|