code-ollama 0.15.0 → 0.15.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.
@@ -169,7 +169,7 @@ var inlineMathExtension = {
169
169
  }
170
170
  };
171
171
  //#endregion
172
- //#region src/components/Markdown/Markdown.tsx
172
+ //#region src/components/Markdown/render.ts
173
173
  var HR_PLACEHOLDER = "__CODE_OLLAMA_HR_PLACEHOLDER__";
174
174
  function renderMarkdown(content, hrWidth) {
175
175
  const hr = "─".repeat(Math.max(1, hrWidth));
@@ -196,10 +196,12 @@ function renderMarkdown(content, hrWidth) {
196
196
  const result = markdown.parse(content);
197
197
  return (typeof result === "string" ? result.trim() : content).replaceAll(HR_PLACEHOLDER, hr);
198
198
  } catch {
199
+ // v8 ignore next
199
200
  return content;
200
201
  }
201
- // v8 ignore stop
202
202
  }
203
+ //#endregion
204
+ //#region src/components/Markdown/Markdown.tsx
203
205
  var Markdown = memo(function Markdown({ content, color, dimColor }) {
204
206
  const { stdout } = useStdout();
205
207
  const availableWidth = stdout.columns - 4;
@@ -217,7 +219,72 @@ var TURN_ABORTED_MESSAGE = [
217
219
  "</turn_aborted>"
218
220
  ].join("\n");
219
221
  //#endregion
220
- //#region src/components/Messages/utils.ts
222
+ //#region src/components/Messages/layout.ts
223
+ var ANSI_REGEX = new RegExp(String.raw`\u001B\[[0-9;]*m`, "g");
224
+ var CODE_BLOCK_MARGIN_Y = 2;
225
+ var CODE_BLOCK_BORDER_Y = 2;
226
+ var CODE_BLOCK_CHROME_X = 4;
227
+ function stripAnsi(value) {
228
+ return value.replaceAll(ANSI_REGEX, "");
229
+ }
230
+ function countLineWidth(value) {
231
+ return Array.from(stripAnsi(value)).length;
232
+ }
233
+ /**
234
+ * Counts the number of wrapped lines for a given content and width.
235
+ *
236
+ * This function splits the content by newlines and calculates how many lines
237
+ * each segment would wrap to based on the available width.
238
+ *
239
+ * @param content The text content to wrap.
240
+ * @param width The available width for wrapping.
241
+ * @returns The number of wrapped lines.
242
+ */
243
+ function countWrappedLines(content, width) {
244
+ const safeWidth = Math.max(1, width);
245
+ return content.split("\n").reduce((lineCount, line) => {
246
+ const visibleWidth = countLineWidth(line);
247
+ return lineCount + Math.max(1, Math.ceil(visibleWidth / safeWidth));
248
+ }, 0);
249
+ }
250
+ /**
251
+ * Calculates the height of a code block based on its content and width.
252
+ *
253
+ * This function accounts for margins, borders, and wrapped lines to determine
254
+ * the total height required for displaying a code block.
255
+ *
256
+ * @param content The code block content to render.
257
+ * @param width The available width for the code block.
258
+ * @returns The total height in lines.
259
+ */
260
+ function getCodeBlockHeight(content, width) {
261
+ const contentWidth = Math.max(1, width - CODE_BLOCK_CHROME_X);
262
+ return CODE_BLOCK_MARGIN_Y + CODE_BLOCK_BORDER_Y + countWrappedLines(content, contentWidth);
263
+ }
264
+ /**
265
+ * Calculates the total height of streaming text content based on wrapped lines.
266
+ *
267
+ * @param textParts Array of text parts with their content and type.
268
+ * @param width The available width for wrapping text.
269
+ * @returns The total height in lines.
270
+ */
271
+ function getStreamingTextHeight(textParts, width) {
272
+ return textParts.reduce((height, part) => {
273
+ const renderMarkdown$1 = renderMarkdown;
274
+ return height + countWrappedLines(part.type === "markdown" ? renderMarkdown$1(part.content, width) : part.content, width);
275
+ }, 0);
276
+ }
277
+ /**
278
+ * Calculates the available width for assistant content after accounting for margins.
279
+ *
280
+ * @param columns The total number of columns in the terminal.
281
+ * @returns The available width for content (always at least 1).
282
+ */
283
+ function getAssistantContentWidth(columns) {
284
+ return Math.max(1, columns - 4);
285
+ }
286
+ //#endregion
287
+ //#region src/components/Messages/parsing.ts
221
288
  var FENCE_LINE_REGEX = /^(?<indent>[ \t]*)(?<fence>`{3,})(?<language>\w+)?[ \t]*$/;
222
289
  function flushTextSegment(segments, textLines) {
223
290
  const textContent = textLines.join("\n").trim();
@@ -303,14 +370,8 @@ function parseContent(content) {
303
370
  flushTextSegment(segments, textLines);
304
371
  return segments;
305
372
  }
306
- function getMessageColor(role) {
307
- switch (role) {
308
- case USER: return "black";
309
- case ASSISTANT: return "cyan";
310
- case SYSTEM: return "gray";
311
- default: return;
312
- }
313
- }
373
+ //#endregion
374
+ //#region src/components/Messages/streaming.ts
314
375
  function isWordCharacter(char) {
315
376
  return char !== void 0 && /[A-Za-z0-9]/.test(char);
316
377
  }
@@ -405,11 +466,27 @@ function splitStreamingInlineContent(content) {
405
466
  return parts;
406
467
  }
407
468
  //#endregion
469
+ //#region src/components/Messages/styles.ts
470
+ function getMessageColor(role) {
471
+ switch (role) {
472
+ case USER: return "black";
473
+ case ASSISTANT: return "cyan";
474
+ case SYSTEM: return "gray";
475
+ default: return;
476
+ }
477
+ }
478
+ //#endregion
408
479
  //#region src/components/Messages/Messages.tsx
409
480
  function Message({ message, isStreaming = false }) {
481
+ const { stdout } = useStdout();
410
482
  const messageColor = getMessageColor(message.role);
411
483
  const isSystem = message.role === SYSTEM;
412
484
  const isUser = message.role === USER;
485
+ const isStreamingAssistant = isStreaming && !isUser && !isSystem;
486
+ const stickyHeightRef = useRef({
487
+ columns: stdout.columns,
488
+ maxHeight: 0
489
+ });
413
490
  if (isSystem) return /* @__PURE__ */ jsx(Box, {
414
491
  flexDirection: "column",
415
492
  marginBottom: 1,
@@ -420,10 +497,23 @@ function Message({ message, isStreaming = false }) {
420
497
  children: message.content
421
498
  })
422
499
  });
423
- return /* @__PURE__ */ jsx(Box, {
500
+ const segments = parseContent(message.content);
501
+ const availableWidth = getAssistantContentWidth(stdout.columns);
502
+ if (stickyHeightRef.current.columns !== stdout.columns) stickyHeightRef.current = {
503
+ columns: stdout.columns,
504
+ maxHeight: 0
505
+ };
506
+ const streamingHeight = isStreamingAssistant ? segments.reduce((height, segment) => {
507
+ if (segment.type === "code") return height + getCodeBlockHeight(segment.content, availableWidth);
508
+ if (segment.type === "raw") return height + getCodeBlockHeight(unwrapRawMarkdownFence(segment.content) ?? segment.content, availableWidth);
509
+ return height + getStreamingTextHeight(splitStreamingInlineContent(segment.content), availableWidth);
510
+ }, 0) : 0;
511
+ if (isStreamingAssistant) stickyHeightRef.current.maxHeight = Math.max(stickyHeightRef.current.maxHeight, streamingHeight);
512
+ const stickyPaddingLines = isStreamingAssistant ? stickyHeightRef.current.maxHeight - streamingHeight : 0;
513
+ return /* @__PURE__ */ jsxs(Box, {
424
514
  flexDirection: "column",
425
515
  marginBottom: 1,
426
- children: parseContent(message.content).map((segment, index) => {
516
+ children: [segments.map((segment, index) => {
427
517
  const prefix = isUser && index === 0 ? "> " : "";
428
518
  if (segment.type === "code") return isUser ? /* @__PURE__ */ jsx(Text, {
429
519
  color: messageColor,
@@ -465,7 +555,7 @@ function Message({ message, isStreaming = false }) {
465
555
  color: messageColor
466
556
  }, partIndex))
467
557
  }, index);
468
- })
558
+ }), Array.from({ length: stickyPaddingLines }, (_, index) => /* @__PURE__ */ jsx(Text, { children: " " }, "padding-" + String(index)))]
469
559
  });
470
560
  }
471
561
  function Messages({ messages, isLoading, sessionId, streamingMessage }) {
package/dist/cli.js CHANGED
@@ -33,7 +33,7 @@ var LIST = [
33
33
  //#endregion
34
34
  //#region package.json
35
35
  var name = "code-ollama";
36
- var version = "0.15.0";
36
+ var version = "0.15.1";
37
37
  //#endregion
38
38
  //#region src/constants/package.ts
39
39
  var NAME = name;
@@ -931,7 +931,7 @@ async function main(args = process.argv.slice(2)) {
931
931
  else await launchTui();
932
932
  }
933
933
  async function launchTui(sessionId) {
934
- const { renderApp } = await import("./assets/tui-DPx5MGHZ.js");
934
+ const { renderApp } = await import("./assets/tui-CSRbnCod.js");
935
935
  reset();
936
936
  renderApp(sessionId);
937
937
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-ollama",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "description": "Ollama coding agent that runs in your terminal",
5
5
  "author": "Mark <mark@remarkablemark.org> (https://remarkablemark.org)",
6
6
  "type": "module",