drexler 0.2.13 → 0.2.15

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.
@@ -1,108 +1,15 @@
1
1
  import { Box, Text } from "ink";
2
- import { memo, useMemo } from "react";
3
- import { renderMarkdown } from "../renderer.ts";
2
+ import { memo } from "react";
3
+ import {
4
+ firstDisplayLine,
5
+ normalizeAssistantDisplayContent,
6
+ normalizeAssistantMarkdownRenderContent,
7
+ } from "./displayContent.ts";
4
8
  import { fitDisplayText } from "./graphemes.ts";
9
+ import { MarkdownBody } from "./MarkdownBody.tsx";
5
10
  import { useTheme } from "./ThemeContext.tsx";
6
11
 
7
- interface MessageItem {
8
- role: "user" | "assistant" | "system";
9
- content: string;
10
- }
11
-
12
- const SEPARATOR_WIDTH = 44;
13
-
14
- const ROLE_LABELS: Record<MessageItem["role"], string> = {
15
- user: "YOU",
16
- assistant: "DREXLER",
17
- system: "SYSTEM",
18
- };
19
-
20
- function Separator() {
21
- const t = useTheme();
22
- return (
23
- <Box paddingX={1} marginBottom={1} flexShrink={1}>
24
- <Text color={t.primaryDim} wrap="truncate">
25
- {"─".repeat(SEPARATOR_WIDTH)}
26
- </Text>
27
- </Box>
28
- );
29
- }
30
-
31
- function MessageInner({ role, content }: MessageItem) {
32
- const t = useTheme();
33
- const assistantLines = useMemo(
34
- () =>
35
- role === "assistant"
36
- ? renderMarkdown(content).trimEnd().split("\n")
37
- : [],
38
- [content, role],
39
- );
40
-
41
- if (role === "user") {
42
- return (
43
- <>
44
- <Box paddingX={1} marginBottom={1} flexDirection="column">
45
- <Box>
46
- <Text color={t.primaryLight} bold>
47
- {ROLE_LABELS.user}
48
- </Text>
49
- <Text color={t.primaryDim}> ─ </Text>
50
- <Text color={t.dim}>incoming memo</Text>
51
- </Box>
52
- <Box paddingLeft={1}>
53
- <Text color={t.primary}>› </Text>
54
- <Text color={t.text} wrap="wrap">
55
- {content}
56
- </Text>
57
- </Box>
58
- </Box>
59
- </>
60
- );
61
- }
62
- if (role === "system") {
63
- return (
64
- <Box paddingX={1} marginBottom={1} flexDirection="column">
65
- <Box>
66
- <Text color={t.warning} bold>
67
- {ROLE_LABELS.system}
68
- </Text>
69
- <Text color={t.primaryDim}> ─ </Text>
70
- <Text color={t.dim}>notice</Text>
71
- </Box>
72
- <Box paddingLeft={1}>
73
- <Text color={t.dim} italic wrap="wrap">
74
- {content}
75
- </Text>
76
- </Box>
77
- </Box>
78
- );
79
- }
80
-
81
- return (
82
- <>
83
- <Box flexDirection="column" marginBottom={1} paddingX={1}>
84
- <Box>
85
- <Text color={t.primaryLight} bold>
86
- {ROLE_LABELS.assistant}
87
- </Text>
88
- <Text color={t.primaryDim}> ─ </Text>
89
- <Text color={t.dim}>response ledger</Text>
90
- </Box>
91
- {assistantLines.map((ln, i) => (
92
- <Box key={i} paddingLeft={1}>
93
- <Text color={i === 0 ? t.primary : t.primaryDim}>│ </Text>
94
- <Text color={t.text} wrap="wrap">
95
- {ln}
96
- </Text>
97
- </Box>
98
- ))}
99
- </Box>
100
- <Separator />
101
- </>
102
- );
103
- }
104
-
105
- export const Message = memo(MessageInner);
12
+ const ROLE_LABELS = { assistant: "DREXLER" } as const;
106
13
 
107
14
  interface StreamingProps {
108
15
  content: string;
@@ -111,12 +18,16 @@ interface StreamingProps {
111
18
 
112
19
  function StreamingMessageInner({ content, width = 80 }: StreamingProps) {
113
20
  const t = useTheme();
114
- const lines = useMemo(() => content.split("\n"), [content]);
115
21
  const safeWidth = Math.max(1, Math.floor(width));
116
- const contentWidth = Math.max(1, safeWidth - 3);
22
+ const innerWidth = Math.max(1, safeWidth - 2);
23
+ const compactDisplayContent = normalizeAssistantDisplayContent(content);
24
+ const markdownDisplayContent = normalizeAssistantMarkdownRenderContent(content);
117
25
 
118
26
  if (safeWidth < 18) {
119
- const compactLine = fitDisplayText(content.replace(/\s+/g, " "), safeWidth);
27
+ const compactLine = fitDisplayText(
28
+ firstDisplayLine(compactDisplayContent).replace(/\s+/g, " "),
29
+ safeWidth,
30
+ );
120
31
  return (
121
32
  <Box width={safeWidth} flexShrink={1}>
122
33
  <Text color={t.primaryLight} wrap="truncate">
@@ -135,16 +46,15 @@ function StreamingMessageInner({ content, width = 80 }: StreamingProps) {
135
46
  <Text color={t.primaryDim}> ─ </Text>
136
47
  <Text color={t.dim}>drafting live</Text>
137
48
  </Box>
138
- {lines.map((ln, i) => (
139
- <Box key={i} paddingLeft={1}>
140
- <Text color={i === lines.length - 1 ? t.primaryLight : t.primary}>
141
- {" "}
142
- </Text>
143
- <Text color={t.text} wrap="truncate">
144
- {fitDisplayText(ln, contentWidth)}
145
- </Text>
146
- </Box>
147
- ))}
49
+ <MarkdownBody
50
+ content={markdownDisplayContent}
51
+ baseColor={t.text}
52
+ accentColor={t.primaryLight}
53
+ dimColor={t.dim}
54
+ codeColor={t.primaryDim}
55
+ width={innerWidth}
56
+ paddingLeft={1}
57
+ />
148
58
  </Box>
149
59
  );
150
60
  }