@codemem/opencode-plugin 0.44.2 → 0.45.0-rc.2

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/tui.js ADDED
@@ -0,0 +1,105 @@
1
+ import { CodememNotifications } from "./rpc.js";
2
+
3
+ const DEFAULT_MAX_SEEN_NOTICES = 64;
4
+ const DEFAULT_REPLAY_TIMEOUT_MS = 250;
5
+ const TOAST_VARIANTS = new Set(["info", "success", "warning", "error"]);
6
+
7
+ const sameLocation = (left, right) =>
8
+ left?.directory === right?.directory &&
9
+ (left?.workspaceID ?? null) === (right?.workspaceID ?? null);
10
+
11
+ const normalizeNotice = (value) => {
12
+ if (!value || typeof value !== "object") return null;
13
+ const id = typeof value.id === "string" ? value.id : "";
14
+ const message = typeof value.message === "string" ? value.message : "";
15
+ const variant = TOAST_VARIANTS.has(value.variant) ? value.variant : "warning";
16
+ if (!id || !message) return null;
17
+ return { id, message, variant };
18
+ };
19
+
20
+ export const createCodememTuiPlugin = ({
21
+ maxSeenNotices = DEFAULT_MAX_SEEN_NOTICES,
22
+ replayTimeoutMs = DEFAULT_REPLAY_TIMEOUT_MS,
23
+ } = {}) =>
24
+ ({
25
+ id: "codemem.tui",
26
+ async setup(context) {
27
+ const seen = new Set();
28
+ let active = true;
29
+ const seenLimit =
30
+ Number.isSafeInteger(maxSeenNotices) && maxSeenNotices > 0
31
+ ? maxSeenNotices
32
+ : DEFAULT_MAX_SEEN_NOTICES;
33
+ const location = context.location ?? context.data?.location?.default?.();
34
+ const show = (input) => {
35
+ if (!active) return;
36
+ const notice = normalizeNotice(input);
37
+ if (!notice || seen.has(notice.id)) return;
38
+ seen.add(notice.id);
39
+ while (seen.size > seenLimit) seen.delete(seen.values().next().value);
40
+ try {
41
+ context.ui.toast.show({
42
+ title: "Codemem",
43
+ message: notice.message,
44
+ variant: notice.variant,
45
+ });
46
+ } catch {
47
+ // Notifications must not affect the TUI plugin lifecycle.
48
+ }
49
+ };
50
+
51
+ let notifications;
52
+ try {
53
+ notifications = context.client.rpc(CodememNotifications);
54
+ } catch {
55
+ return undefined;
56
+ }
57
+
58
+ let unsubscribe = () => {};
59
+ try {
60
+ unsubscribe = notifications.events.on("notice", (event) => {
61
+ if (location && !sameLocation(event.location, location)) return;
62
+ show(event.data);
63
+ });
64
+ } catch {
65
+ // Replay can still deliver notices when live subscription is unavailable.
66
+ }
67
+
68
+ const replayAbort = new AbortController();
69
+ const replayLimit =
70
+ Number.isSafeInteger(replayTimeoutMs) && replayTimeoutMs > 0
71
+ ? replayTimeoutMs
72
+ : DEFAULT_REPLAY_TIMEOUT_MS;
73
+ let replayTimer;
74
+ const replayTimeout = new Promise((resolve) => {
75
+ replayTimer = setTimeout(() => {
76
+ replayAbort.abort();
77
+ resolve(null);
78
+ }, replayLimit);
79
+ replayTimer.unref?.();
80
+ });
81
+ const replayTask = Promise.resolve()
82
+ .then(() =>
83
+ notifications.drain({}, {
84
+ ...(location ? { location } : {}),
85
+ signal: replayAbort.signal,
86
+ }),
87
+ )
88
+ .catch(() => null);
89
+ const replay = await Promise.race([replayTask, replayTimeout]);
90
+ clearTimeout(replayTimer);
91
+ for (const notice of Array.isArray(replay?.notices) ? replay.notices : []) show(notice);
92
+
93
+ return () => {
94
+ active = false;
95
+ replayAbort.abort();
96
+ try {
97
+ unsubscribe();
98
+ } catch {
99
+ // Best-effort cleanup only.
100
+ }
101
+ };
102
+ },
103
+ });
104
+
105
+ export default createCodememTuiPlugin();
@@ -0,0 +1 @@
1
+ export { default } from "../src/opencode-v2-contract-fixture.ts";
@@ -0,0 +1,8 @@
1
+ {
2
+ "private": true,
3
+ "type": "module",
4
+ "main": "./index.ts",
5
+ "peerDependencies": {
6
+ "@opencode/plugin": "2.0.2"
7
+ }
8
+ }
@@ -1 +0,0 @@
1
- export { default } from "./codemem.js";