paseo-terminal-integration 0.5.1 → 0.5.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.
@@ -4,6 +4,13 @@ import {
4
4
  type PluginTimelineTransformerContribution,
5
5
  } from "@getpaseo/plugin/client";
6
6
  import { Icon } from "@getpaseo/plugin/client/react-native";
7
+ import {
8
+ darkHighlightColors,
9
+ highlightCode,
10
+ lightHighlightColors,
11
+ type HighlightStyle,
12
+ type HighlightToken,
13
+ } from "@getpaseo/highlight";
7
14
  import { useMemo, useState } from "react";
8
15
  import { Platform, Pressable, ScrollView, Text, View } from "react-native";
9
16
  import {
@@ -42,7 +49,12 @@ export function RunReport({ item, theme, layout }: PluginTimelineItemProps<RunRe
42
49
  return (
43
50
  <View style={styles.card}>
44
51
  <Text style={styles.intro}>{item.data.intro}</Text>
45
- <CodeBlock text={item.data.code} styles={styles} />
52
+ <CodeBlock
53
+ text={item.data.code}
54
+ language={item.data.language}
55
+ styles={styles}
56
+ theme={theme}
57
+ />
46
58
  <Text style={styles.outputLabel}>{item.data.outputLabel}</Text>
47
59
  </View>
48
60
  );
@@ -71,7 +83,12 @@ export function RunReport({ item, theme, layout }: PluginTimelineItemProps<RunRe
71
83
  </Pressable>
72
84
  {expanded ? (
73
85
  <>
74
- <CodeBlock text={item.data.code} styles={styles} />
86
+ <CodeBlock
87
+ text={item.data.code}
88
+ language={item.data.language}
89
+ styles={styles}
90
+ theme={theme}
91
+ />
75
92
  <Text style={styles.outputLabel}>{item.data.outputLabel}</Text>
76
93
  <CodeBlock text={item.data.output} styles={styles} />
77
94
  </>
@@ -82,16 +99,88 @@ export function RunReport({ item, theme, layout }: PluginTimelineItemProps<RunRe
82
99
 
83
100
  type Styles = ReturnType<typeof createStyles>;
84
101
 
85
- function CodeBlock({ text, styles }: { text: string; styles: Styles }) {
102
+ const MAX_HIGHLIGHT_CHARS = 100_000;
103
+
104
+ const LANGUAGE_EXTENSIONS: Readonly<Record<string, string>> = {
105
+ bash: "sh",
106
+ shell: "sh",
107
+ console: "sh",
108
+ zsh: "sh",
109
+ fish: "sh",
110
+ python: "py",
111
+ py: "py",
112
+ javascript: "js",
113
+ node: "js",
114
+ typescript: "ts",
115
+ };
116
+
117
+ function CodeBlock({
118
+ text,
119
+ language,
120
+ styles,
121
+ theme,
122
+ }: {
123
+ text: string;
124
+ language?: string;
125
+ styles: Styles;
126
+ theme?: Theme;
127
+ }) {
128
+ const highlighted = useMemo(() => highlight(text, language), [language, text]);
129
+ const colors =
130
+ theme && isDark(theme.colors.surface0) ? darkHighlightColors : lightHighlightColors;
131
+
86
132
  return (
87
133
  <ScrollView horizontal style={styles.codeBlock} contentContainerStyle={styles.codeContent}>
88
134
  <Text selectable style={styles.code}>
89
- {text}
135
+ {highlighted
136
+ ? highlighted.map((line, lineIndex) => (
137
+ <Text key={lineIndex}>
138
+ {line.map((token, tokenIndex) => (
139
+ <Text
140
+ key={`${lineIndex}-${tokenIndex}`}
141
+ style={{ color: tokenColor(token, colors, theme?.colors.foreground) }}
142
+ >
143
+ {token.text}
144
+ </Text>
145
+ ))}
146
+ {lineIndex < highlighted.length - 1 ? "\n" : null}
147
+ </Text>
148
+ ))
149
+ : text}
90
150
  </Text>
91
151
  </ScrollView>
92
152
  );
93
153
  }
94
154
 
155
+ function highlight(text: string, language?: string): HighlightToken[][] | null {
156
+ if (!language || text.length > MAX_HIGHLIGHT_CHARS) return null;
157
+ const normalized = language.toLowerCase();
158
+ const extension = LANGUAGE_EXTENSIONS[normalized] ?? normalized;
159
+ try {
160
+ return highlightCode(text, `command.${extension}`);
161
+ } catch {
162
+ return null;
163
+ }
164
+ }
165
+
166
+ function tokenColor(
167
+ token: HighlightToken,
168
+ colors: Readonly<Record<HighlightStyle, string>>,
169
+ fallback = "inherit",
170
+ ): string {
171
+ return token.style ? colors[token.style] : fallback;
172
+ }
173
+
174
+ function isDark(color: string): boolean {
175
+ const match = /^#([0-9a-f]{6})$/i.exec(color);
176
+ if (!match) return true;
177
+ const value = Number.parseInt(match[1], 16);
178
+ const red = value >> 16;
179
+ const green = (value >> 8) & 0xff;
180
+ const blue = value & 0xff;
181
+ return red * 0.299 + green * 0.587 + blue * 0.114 < 128;
182
+ }
183
+
95
184
  function createStyles(theme: Theme, compact: boolean) {
96
185
  return {
97
186
  card: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paseo-terminal-integration",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Run agent code blocks in a Paseo terminal and send the output back to the agent; attach terminal output to prompts",
5
5
  "keywords": [
6
6
  "agents",
@@ -35,6 +35,9 @@
35
35
  "lint": "oxlint",
36
36
  "format": "oxfmt ."
37
37
  },
38
+ "dependencies": {
39
+ "@getpaseo/highlight": "0.11.2"
40
+ },
38
41
  "devDependencies": {
39
42
  "@getpaseo/plugin": "0.10.2",
40
43
  "@tanstack/react-query": "^5.90.11",