pi-mox 0.1.0 → 0.2.0

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/CHANGELOG.md CHANGED
@@ -5,6 +5,24 @@ version may add commands and options and a patch version fixes.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 0.2.0
9
+
10
+ ### Added
11
+
12
+ - `/mox show <id>` shows one cue with its whole message.
13
+
14
+ ### Changed
15
+
16
+ - `/mox list` and the id completions show each message on one line: whitespace and line breaks collapse to
17
+ one space, and a message over 60 characters is cut with `…`. `/mox show <id>` gives the whole message.
18
+
19
+ ## 0.1.1
20
+
21
+ ### Added
22
+
23
+ - A gallery preview image (`pi.image`) in the Roman inscription look, also at the top of the README;
24
+ its source is `assets/preview.html`.
25
+
8
26
  ## 0.1.0
9
27
 
10
28
  ### Added
package/README.md CHANGED
@@ -1,4 +1,6 @@
1
- # pi-mox
1
+ # ·MOX· pi-mox
2
+
3
+ ![pi-mox: schedule a message for the agent in Pi](assets/preview.png)
2
4
 
3
5
  Schedule a message for the agent in [Pi](https://github.com/earendil-works/pi). Say when, say what, and at
4
6
  that time the message reaches the agent and starts a turn. *Mox* is Latin for "soon".
@@ -21,10 +23,11 @@ pi install npm:pi-mox
21
23
  | Command | What it does |
22
24
  | --- | --- |
23
25
  | `/mox <time> <message>` | Deliver `<message>` to the agent at `<time>` |
24
- | `/mox list` (or `/mox`) | Show the pending cues |
26
+ | `/mox list` (or `/mox`) | Show the pending cues, each message on one line cut at 60 characters with `…` |
27
+ | `/mox show <id>` | Show one cue with its whole message |
25
28
  | `/mox cancel <id>` | Cancel a cue; the id is Roman (`II`, `ii`) or Arabic (`2`) |
26
29
 
27
- `list` and `cancel` are reserved first words.
30
+ `list`, `show` and `cancel` are reserved first words.
28
31
 
29
32
  ### Time
30
33
 
@@ -48,6 +51,10 @@ Scheduled II for 14:00 (in 5h36m): corre la suite
48
51
  I 10:30 2h06m continúa con T3
49
52
  II 14:00 5h36m corre la suite
50
53
 
54
+ /mox show I
55
+ I 10:30 2h06m
56
+ continúa con T3
57
+
51
58
  /mox cancel II
52
59
  Cancelled II (14:00): corre la suite
53
60
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mox",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Schedule a message for the agent in Pi: /mox 10:30 <message> delivers it at that time and starts a turn.",
5
5
  "license": "MIT",
6
6
  "author": "jals1212",
@@ -22,7 +22,8 @@
22
22
  "pi": {
23
23
  "extensions": [
24
24
  "./extensions/mox.ts"
25
- ]
25
+ ],
26
+ "image": "https://raw.githubusercontent.com/jals1212/pi-mox/main/assets/preview.png"
26
27
  },
27
28
  "files": [
28
29
  "src",
package/src/mox.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // pi-mox core: time parsing, Roman numerals, the cue list and its wall-clock scheduler, and the /mox command.
2
2
  // No Pi runtime imports here (see src/wire.ts for the wiring), so everything runs under plain `node --test`.
3
3
 
4
- export const USAGE = "Usage: /mox <time> <message>, /mox list, /mox cancel <id> (time: 10:30 or +2h15m)";
4
+ export const USAGE = "Usage: /mox <time> <message>, /mox list, /mox show <id>, /mox cancel <id> (time: 10:30 or +2h15m)";
5
5
 
6
6
  /** A pending cue: a message to deliver at `at` (epoch milliseconds). `id` is shown as a Roman numeral. */
7
7
  export interface Cue {
@@ -236,6 +236,16 @@ export function createMox(options: MoxOptions): Mox {
236
236
 
237
237
  // --- Command ---
238
238
 
239
+ const PREVIEW_CHARS = 60;
240
+
241
+ /** One-line preview for lists: whitespace runs collapse to one space; over 60 characters it ends in `…`. */
242
+ export function previewMessage(message: string): string {
243
+ const line = message.replace(/\s+/g, " ").trim();
244
+ return line.length > PREVIEW_CHARS ? `${line.slice(0, PREVIEW_CHARS - 1).trimEnd()}…` : line;
245
+ }
246
+
247
+ const parseId = (text: string) => (/^\d+$/.test(text) ? Number(text) : fromRoman(text));
248
+
239
249
  const dayStart = (ms: number) => new Date(new Date(ms).setHours(0, 0, 0, 0)).getTime();
240
250
 
241
251
  function dayLabel(at: number, now: number): string {
@@ -265,16 +275,26 @@ export function runMoxCommand(args: string, deps: CommandDeps): void {
265
275
  return info(
266
276
  cues
267
277
  .map((cue) => {
268
- const line = `${formatClock(cue.at)} ${formatRemaining(cue.at - now)} ${cue.message}`;
278
+ const line = `${formatClock(cue.at)} ${formatRemaining(cue.at - now)} ${previewMessage(cue.message)}`;
269
279
  return `${paint(BRONZE, toRoman(cue.id).padEnd(width), ctx.hasUI)} ${paint(MARBLE, line, ctx.hasUI)}`;
270
280
  })
271
281
  .join("\n"),
272
282
  );
273
283
  }
274
284
 
285
+ if (word === "show") {
286
+ if (rest === "") return fail("Missing cue id.");
287
+ const id = parseId(rest);
288
+ if (id === undefined || id < 1) return fail(`Invalid cue id "${rest}".`);
289
+ const cue = mox.list().find((candidate) => candidate.id === id);
290
+ if (!cue) return fail(`No pending cue ${toRoman(id)}.`);
291
+ const head = `${toRoman(cue.id)} ${formatClock(cue.at)} ${formatRemaining(cue.at - deps.now())}`;
292
+ return info(`${paint(BRONZE, head, ctx.hasUI)}\n${paint(MARBLE, cue.message, ctx.hasUI)}`);
293
+ }
294
+
275
295
  if (word === "cancel") {
276
296
  if (rest === "") return fail("Missing cue id.");
277
- const id = /^\d+$/.test(rest) ? Number(rest) : fromRoman(rest);
297
+ const id = parseId(rest);
278
298
  if (id === undefined || id < 1) return fail(`Invalid cue id "${rest}".`);
279
299
  const cue = mox.cancel(id);
280
300
  if (!cue) return fail(`No pending cue ${toRoman(id)}.`);
@@ -289,20 +309,24 @@ export function runMoxCommand(args: string, deps: CommandDeps): void {
289
309
  info(`Scheduled ${toRoman(cue.id)} for ${formatClock(at)}${dayLabel(at, now)} (in ${formatRemaining(at - now)}): ${rest}`);
290
310
  }
291
311
 
292
- /** Autocompletion: `list`, `cancel`, and pending ids after `cancel`. */
312
+ /** Autocompletion: `list`, `show`, `cancel`, and pending ids after `show` or `cancel`. */
293
313
  export function completeMoxArgs(prefix: string, mox: Mox): { value: string; label: string }[] | null {
294
314
  const words = prefix.split(" ");
295
315
  if (words.length === 1) {
296
- const items = ["list", "cancel"].filter((sub) => sub.startsWith(words[0])).map((sub) => ({ value: sub, label: sub }));
316
+ const items = ["list", "show", "cancel"].filter((sub) => sub.startsWith(words[0])).map((sub) => ({ value: sub, label: sub }));
297
317
  return items.length > 0 ? items : null;
298
318
  }
299
- if (words.length !== 2 || words[0] !== "cancel") return null;
319
+ const sub = words[0];
320
+ if (words.length !== 2 || (sub !== "cancel" && sub !== "show")) return null;
300
321
  const typed = words[1].toUpperCase();
301
322
  const items = mox
302
323
  .list()
303
324
  .sort((a, b) => a.id - b.id)
304
325
  .filter((cue) => toRoman(cue.id).startsWith(typed) || String(cue.id).startsWith(typed))
305
- .map((cue) => ({ value: `cancel ${toRoman(cue.id)}`, label: `${toRoman(cue.id)} ${formatClock(cue.at)} ${cue.message}` }));
326
+ .map((cue) => ({
327
+ value: `${sub} ${toRoman(cue.id)}`,
328
+ label: `${toRoman(cue.id)} ${formatClock(cue.at)} ${previewMessage(cue.message)}`,
329
+ }));
306
330
  return items.length > 0 ? items : null;
307
331
  }
308
332
 
package/src/wire.ts CHANGED
@@ -50,7 +50,7 @@ export function registerMox(pi: ExtensionAPI, options: WireOptions): void {
50
50
  });
51
51
 
52
52
  pi.registerCommand("mox", {
53
- description: "Schedule a message for the agent: /mox <10:30|+2h15m> <message>, /mox list, /mox cancel <id>",
53
+ description: "Schedule a message for the agent: /mox <10:30|+2h15m> <message>, /mox list, /mox show <id>, /mox cancel <id>",
54
54
  getArgumentCompletions: (prefix) => completeMoxArgs(prefix, mox),
55
55
  handler: async (args, ctx) => {
56
56
  use(ctx);