@lawaty/lacode 1.3.0-beta.4 → 1.3.0-beta.6

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/dist/tui.js CHANGED
@@ -822,56 +822,72 @@ function createVoiceClient(options, deps) {
822
822
  }
823
823
 
824
824
  // src/tui/timeline.tsx
825
- import { createSignal, createEffect, onCleanup, For } from "solid-js";
826
825
  import { readFileSync as readFileSync3 } from "node:fs";
827
826
  import { homedir as homedir2 } from "node:os";
828
827
  import { join as join3, resolve } from "node:path";
829
828
  import { jsx, jsxs } from "@opentui/solid/jsx-runtime";
830
829
  var registered = false;
830
+ var ENTRY_LINES = 14;
831
+ var readEntries = (id) => {
832
+ if (typeof id !== "string" || !/^[A-Za-z0-9_.-]+$/.test(id)) return [];
833
+ const rawDir = process.env.LACODE_TIMELINE_DIR;
834
+ const dir = rawDir ? resolve(rawDir) : join3(homedir2(), ".local/share/opencode/lacode/timeline");
835
+ try {
836
+ const raw = readFileSync3(join3(dir, id + ".jsonl"), "utf8");
837
+ const parsed = [];
838
+ for (const line of raw.split("\n")) {
839
+ const trimmed = line.trim();
840
+ if (!trimmed) continue;
841
+ try {
842
+ const obj = JSON.parse(trimmed);
843
+ if (obj && typeof obj.ts === "number" && typeof obj.text === "string") parsed.push(obj.text);
844
+ } catch {
845
+ }
846
+ }
847
+ return parsed.slice(-ENTRY_LINES).reverse();
848
+ } catch {
849
+ return [];
850
+ }
851
+ };
831
852
  var tui = async (api) => {
832
853
  if (registered) return;
833
854
  registered = true;
834
855
  api.slots.register({
835
856
  order: 150,
836
857
  slots: {
858
+ // The rows are static and their text is written through the renderable's
859
+ // `content` setter. Solid signals are deliberately NOT used here: the
860
+ // plugin's `solid-js` is a different instance from the TUI renderer's, so
861
+ // signal updates never reach the screen. Plain renderables always do.
837
862
  sidebar_content(ctx, props) {
838
- const [entries, setEntries] = createSignal([]);
839
- const read = (id) => {
840
- if (typeof id !== "string" || !/^[A-Za-z0-9_.-]+$/.test(id)) {
841
- setEntries([]);
842
- return;
843
- }
844
- const rawDir = process.env.LACODE_TIMELINE_DIR;
845
- const dir = rawDir ? resolve(rawDir) : join3(homedir2(), ".local/share/opencode/lacode/timeline");
846
- const file = join3(dir, id + ".jsonl");
847
- try {
848
- const raw = readFileSync3(file, "utf8");
849
- const parsed = [];
850
- for (const line of raw.split("\n")) {
851
- const trimmed = line.trim();
852
- if (!trimmed) continue;
853
- try {
854
- const obj = JSON.parse(trimmed);
855
- if (obj && typeof obj.ts === "number" && typeof obj.text === "string") {
856
- parsed.push(obj.text);
857
- }
858
- } catch {
859
- }
860
- }
861
- setEntries(parsed.slice(-14).reverse());
862
- } catch {
863
- setEntries([]);
863
+ const rows = [];
864
+ const muted = ctx.theme.current.textMuted;
865
+ const paint = () => {
866
+ const entries = readEntries(props.session_id);
867
+ for (let i = 0; i < ENTRY_LINES; i++) {
868
+ const row = rows[i];
869
+ if (!row) continue;
870
+ const text = entries[i];
871
+ row.content = text ? `\u2022 ${text}` : i === 0 && entries.length === 0 ? "No timeline yet" : "";
872
+ row.requestRender?.();
864
873
  }
865
874
  };
866
- createEffect(() => {
867
- const id = props.session_id;
868
- read(id);
869
- const timer = setInterval(() => read(id), 1e3);
870
- onCleanup(() => clearInterval(timer));
871
- });
875
+ const timer = setInterval(paint, 1e3);
876
+ timer.unref?.();
877
+ api.lifecycle.onDispose(() => clearInterval(timer));
872
878
  return /* @__PURE__ */ jsxs("box", { flexDirection: "column", children: [
873
- /* @__PURE__ */ jsx("text", { fg: ctx.theme.current.textMuted, children: "Timeline" }),
874
- entries().length === 0 ? /* @__PURE__ */ jsx("text", { fg: ctx.theme.current.textMuted, children: "No timeline yet" }) : /* @__PURE__ */ jsx(For, { each: entries(), children: (text) => /* @__PURE__ */ jsx("text", { children: `\u2022 ${text}` }) })
879
+ /* @__PURE__ */ jsx("text", { fg: muted, children: "Timeline" }),
880
+ Array.from({ length: ENTRY_LINES }, (_, index) => /* @__PURE__ */ jsx(
881
+ "text",
882
+ {
883
+ fg: index === 0 ? muted : void 0,
884
+ ref: (node) => {
885
+ rows[index] = node;
886
+ if (index === ENTRY_LINES - 1) paint();
887
+ },
888
+ children: ""
889
+ }
890
+ ))
875
891
  ] });
876
892
  }
877
893
  }
@@ -1112,6 +1128,16 @@ async function answerWithText(api, opts, text, request) {
1112
1128
  toast(api, { variant: "success", message: labels });
1113
1129
  }
1114
1130
  var tui2 = async (api, options) => {
1131
+ const timelineOpts = options ?? {};
1132
+ const timelineOff = timelineOpts.timeline === false || typeof timelineOpts.timeline === "object" && timelineOpts.timeline !== null && timelineOpts.timeline.enabled === false;
1133
+ if (!timelineOff) {
1134
+ try {
1135
+ await timeline_default.tui(api, options);
1136
+ } catch (error) {
1137
+ void api.client?.app?.log?.({ body: { service: "timeline", level: "warn", message: `timeline sidebar registration failed: ${String(error)}` } })?.catch?.(() => {
1138
+ });
1139
+ }
1140
+ }
1115
1141
  const opts = resolveOptions(options);
1116
1142
  if (!opts.enabled) return;
1117
1143
  daemonConnected = false;
@@ -1333,14 +1359,6 @@ var tui2 = async (api, options) => {
1333
1359
  bindings.push({ key: opts.answerKey, cmd: "lacode.voice.answer", desc: "Voice answer" });
1334
1360
  }
1335
1361
  api.keymap.registerLayer({ mode: "base", commands, bindings });
1336
- const timelineOpts = options ?? {};
1337
- const timelineOff = timelineOpts.timeline === false || typeof timelineOpts.timeline === "object" && timelineOpts.timeline !== null && timelineOpts.timeline.enabled === false;
1338
- if (!timelineOff) {
1339
- try {
1340
- await timeline_default.tui(api, options);
1341
- } catch {
1342
- }
1343
- }
1344
1362
  };
1345
1363
  var mod = {
1346
1364
  id: "lacode.voice",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lawaty/lacode",
3
- "version": "1.3.0-beta.4",
3
+ "version": "1.3.0-beta.6",
4
4
  "description": "Ultimate opencode setup for Go users — cost-controlled pooled subagents, hang recovery, and notifications for opencode.",
5
5
  "license": "MIT",
6
6
  "type": "module",