@giovannijecha/jecode 0.6.0 → 0.7.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/dist/tui/app.js CHANGED
@@ -32,6 +32,7 @@ export async function runApp(session, transcriptRoot, environment = {}) {
32
32
  const workspace = await workspaceLabel(session.config.root);
33
33
  const state = appState();
34
34
  state.blocks.push(...session.conversation.transcript);
35
+ state.committedNodeId = session.conversation.activeNodeId;
35
36
  const permissions = sessionPermissions(session.tools, session.config.autoApprove);
36
37
  let closed;
37
38
  let frameTimer;
@@ -39,12 +40,21 @@ export async function runApp(session, transcriptRoot, environment = {}) {
39
40
  let escapeTimer;
40
41
  let stopResize = () => { };
41
42
  let stopInput = () => { };
43
+ let failure;
42
44
  // Timers outlive the teardown they were scheduled before. Painting after the
43
45
  // terminal has been handed back would write escapes into the user's shell.
44
46
  let live = true;
45
47
  const done = new Promise((resolve) => {
46
48
  closed = resolve;
47
49
  });
50
+ const guard = (action) => {
51
+ try {
52
+ action();
53
+ }
54
+ catch (error) {
55
+ fail(error);
56
+ }
57
+ };
48
58
  const view = () => {
49
59
  const now = Date.now();
50
60
  return {
@@ -93,8 +103,9 @@ export async function runApp(session, transcriptRoot, environment = {}) {
93
103
  const render = (block) => {
94
104
  if (block !== undefined)
95
105
  transcript.invalidate(block);
96
- if (live && frameTimer === undefined)
97
- frameTimer = setTimeout(draw, FRAME_MS);
106
+ if (live && frameTimer === undefined) {
107
+ frameTimer = setTimeout(() => guard(draw), FRAME_MS);
108
+ }
98
109
  };
99
110
  const feedback = feedbackController((next) => {
100
111
  state.feedback = next;
@@ -116,6 +127,16 @@ export async function runApp(session, transcriptRoot, environment = {}) {
116
127
  if (state.follow)
117
128
  state.unseen = 0;
118
129
  };
130
+ const replaceTranscript = () => {
131
+ state.blocks.splice(0, state.blocks.length, ...session.conversation.transcript);
132
+ state.scroll = 0;
133
+ state.follow = true;
134
+ state.unseen = 0;
135
+ state.lastMaxScroll = 0;
136
+ transcript.invalidate();
137
+ paint.invalidate();
138
+ render();
139
+ };
119
140
  function quit() {
120
141
  if (!live)
121
142
  return;
@@ -132,6 +153,10 @@ export async function runApp(session, transcriptRoot, environment = {}) {
132
153
  terminal.leave();
133
154
  closed?.();
134
155
  }
156
+ function fail(error) {
157
+ failure ??= { error };
158
+ quit();
159
+ }
135
160
  function requestQuit() {
136
161
  const activity = state.activity;
137
162
  if (activity === undefined) {
@@ -148,7 +173,7 @@ export async function runApp(session, transcriptRoot, environment = {}) {
148
173
  const activity = begin(kind, label);
149
174
  state.activity = activity;
150
175
  state.status = label;
151
- spinTimer = setInterval(() => {
176
+ spinTimer = setInterval(() => guard(() => {
152
177
  if (!session.config.reducedMotion)
153
178
  state.spin++;
154
179
  let activeTool;
@@ -160,7 +185,7 @@ export async function runApp(session, transcriptRoot, environment = {}) {
160
185
  break;
161
186
  }
162
187
  render(activeTool);
163
- }, session.config.reducedMotion ? 1_000 : SPIN_MS);
188
+ }), session.config.reducedMotion ? 1_000 : SPIN_MS);
164
189
  render();
165
190
  return activity;
166
191
  }
@@ -187,6 +212,7 @@ export async function runApp(session, transcriptRoot, environment = {}) {
187
212
  emit,
188
213
  commandNotice,
189
214
  render,
215
+ replaceTranscript,
190
216
  refreshSettings: () => {
191
217
  terminal.setReducedMotion(session.config.reducedMotion);
192
218
  paint.invalidate();
@@ -229,14 +255,9 @@ export async function runApp(session, transcriptRoot, environment = {}) {
229
255
  return;
230
256
  try {
231
257
  await launch.open(candidate.id);
232
- state.blocks.splice(0, state.blocks.length, ...session.conversation.transcript);
258
+ replaceTranscript();
259
+ state.committedNodeId = session.conversation.activeNodeId;
233
260
  state.past.length = 0;
234
- state.scroll = 0;
235
- state.follow = true;
236
- state.unseen = 0;
237
- state.lastMaxScroll = 0;
238
- transcript.invalidate();
239
- paint.invalidate();
240
261
  finishActivity(activity);
241
262
  return;
242
263
  }
@@ -246,39 +267,47 @@ export async function runApp(session, transcriptRoot, environment = {}) {
246
267
  }
247
268
  }
248
269
  }
249
- terminal.enter(session.config.reducedMotion);
250
- stopResize = terminal.onResize(() => {
251
- paint.invalidate();
252
- draw();
253
- });
254
- stopInput = terminal.onInput((chunk) => {
255
- if (escapeTimer !== undefined)
256
- clearTimeout(escapeTimer);
257
- for (const key of keys.push(chunk)) {
258
- if (!live)
259
- break;
260
- input.handle(key);
261
- }
262
- if (!live)
263
- return;
264
- escapeTimer = setTimeout(() => {
265
- escapeTimer = undefined;
270
+ try {
271
+ terminal.enter(session.config.reducedMotion);
272
+ stopResize = terminal.onResize(() => guard(() => {
273
+ paint.invalidate();
274
+ draw();
275
+ }));
276
+ stopInput = terminal.onInput((chunk) => guard(() => {
277
+ if (escapeTimer !== undefined)
278
+ clearTimeout(escapeTimer);
279
+ for (const key of keys.push(chunk)) {
280
+ if (!live)
281
+ break;
282
+ input.handle(key);
283
+ }
266
284
  if (!live)
267
285
  return;
268
- for (const key of keys.flush())
269
- input.handle(key);
270
- if (live)
271
- render();
272
- }, ESCAPE_MS);
273
- render();
274
- });
275
- draw();
276
- try {
277
- await resumeAtLaunch;
286
+ escapeTimer = setTimeout(() => guard(() => {
287
+ escapeTimer = undefined;
288
+ if (!live)
289
+ return;
290
+ for (const key of keys.flush())
291
+ input.handle(key);
292
+ if (live)
293
+ render();
294
+ }), ESCAPE_MS);
295
+ render();
296
+ }));
297
+ draw();
298
+ if (resumeAtLaunch !== undefined)
299
+ await Promise.race([resumeAtLaunch, done]);
278
300
  if (live)
279
301
  await done;
302
+ if (failure !== undefined)
303
+ throw failure.error;
280
304
  }
281
305
  finally {
282
- await session.persistence?.close();
306
+ try {
307
+ quit();
308
+ }
309
+ finally {
310
+ await session.persistence?.close();
311
+ }
283
312
  }
284
313
  }
@@ -5,11 +5,13 @@ export function renderNotice(block, width, pal) {
5
5
  warn: pal.ink.attention,
6
6
  error: pal.ink.removed,
7
7
  };
8
- const mark = block.tone === "error" ? "× " : block.tone === "warn" ? "! " : "· ";
8
+ const mark = block.tone === "error" ? "× " : block.tone === "warn" ? "! " : undefined;
9
9
  return [
10
10
  "",
11
11
  ...wrap(block.text, Math.max(1, width - 3)).map((line, index) => row(width, [
12
- { text: index === 0 ? mark : " ", fg: fg[block.tone], bold: index === 0 },
12
+ ...(mark === undefined
13
+ ? []
14
+ : [{ text: index === 0 ? mark : " ", fg: fg[block.tone], bold: index === 0 }]),
13
15
  { text: line, fg: fg[block.tone] },
14
16
  ], [], undefined, 1)),
15
17
  ];
@@ -1,6 +1,6 @@
1
1
  // One interaction model for every terminal selector.
2
2
  import { row } from "../ui/render.js";
3
- import { elide } from "../ui/width.js";
3
+ import { elide, graphemes } from "../ui/width.js";
4
4
  import { menuWindow, renderMenuRows } from "./components/menu.js";
5
5
  import { promptCursor, promptLine } from "./components/prompt.js";
6
6
  const WINDOW = 6;
@@ -33,7 +33,7 @@ export function type(picker, text) {
33
33
  export function backspace(picker) {
34
34
  if (picker.searchable !== true || (picker.query ?? "") === "")
35
35
  return picker;
36
- return withQuery(picker, Array.from(picker.query ?? "").slice(0, -1).join(""));
36
+ return withQuery(picker, graphemes(picker.query ?? "").slice(0, -1).join(""));
37
37
  }
38
38
  export function clear(picker) {
39
39
  return withQuery(picker, "");
@@ -38,5 +38,8 @@ export function terminalText(text, options = {}) {
38
38
  return safe;
39
39
  }
40
40
  function isBidiControl(code) {
41
- return (code >= 0x202a && code <= 0x202e) || (code >= 0x2066 && code <= 0x2069);
41
+ return code === 0x061c ||
42
+ (code >= 0x200e && code <= 0x200f) ||
43
+ (code >= 0x202a && code <= 0x202e) ||
44
+ (code >= 0x2066 && code <= 0x2069);
42
45
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@giovannijecha/jecode",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "An owned coding agent with zero external runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "repository": {