@oh-my-pi/pi-coding-agent 11.2.3 → 11.4.0
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/CHANGELOG.md +119 -4
- package/examples/extensions/plan-mode.ts +1 -1
- package/examples/hooks/qna.ts +1 -1
- package/examples/hooks/status-line.ts +1 -1
- package/examples/sdk/11-sessions.ts +1 -1
- package/package.json +8 -8
- package/src/cli/args.ts +9 -6
- package/src/cli/update-cli.ts +2 -2
- package/src/commands/index/index.ts +2 -5
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/changelog/index.ts +2 -2
- package/src/config/keybindings.ts +16 -1
- package/src/config/model-registry.ts +25 -20
- package/src/config/model-resolver.ts +8 -8
- package/src/config/resolve-config-value.ts +92 -0
- package/src/config/settings-schema.ts +9 -0
- package/src/config.ts +14 -1
- package/src/export/html/template.css +7 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +33 -16
- package/src/extensibility/custom-commands/bundled/review/index.ts +1 -1
- package/src/extensibility/extensions/index.ts +18 -0
- package/src/extensibility/extensions/loader.ts +15 -0
- package/src/extensibility/extensions/runner.ts +78 -1
- package/src/extensibility/extensions/types.ts +131 -5
- package/src/extensibility/extensions/wrapper.ts +1 -1
- package/src/extensibility/plugins/git-url.ts +270 -0
- package/src/extensibility/plugins/index.ts +2 -0
- package/src/extensibility/slash-commands.ts +45 -0
- package/src/index.ts +7 -0
- package/src/lsp/render.ts +50 -43
- package/src/lsp/utils.ts +2 -2
- package/src/main.ts +11 -10
- package/src/mcp/transports/stdio.ts +3 -5
- package/src/modes/components/custom-message.ts +0 -8
- package/src/modes/components/diff.ts +41 -13
- package/src/modes/components/footer.ts +4 -4
- package/src/modes/components/model-selector.ts +4 -0
- package/src/modes/components/todo-display.ts +13 -3
- package/src/modes/components/tool-execution.ts +30 -16
- package/src/modes/components/tree-selector.ts +50 -19
- package/src/modes/controllers/event-controller.ts +1 -0
- package/src/modes/controllers/extension-ui-controller.ts +34 -2
- package/src/modes/controllers/input-controller.ts +47 -33
- package/src/modes/controllers/selector-controller.ts +10 -15
- package/src/modes/interactive-mode.ts +50 -38
- package/src/modes/print-mode.ts +6 -0
- package/src/modes/rpc/rpc-client.ts +4 -4
- package/src/modes/rpc/rpc-mode.ts +17 -2
- package/src/modes/rpc/rpc-types.ts +2 -2
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +3 -1
- package/src/patch/applicator.ts +106 -4
- package/src/patch/fuzzy.ts +1 -1
- package/src/patch/shared.ts +77 -63
- package/src/prompts/system/plan-mode-active.md +6 -6
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/ask.md +2 -2
- package/src/prompts/tools/gemini-image.md +2 -2
- package/src/prompts/tools/lsp.md +2 -2
- package/src/prompts/tools/patch.md +1 -1
- package/src/prompts/tools/python.md +3 -3
- package/src/prompts/tools/task.md +7 -1
- package/src/prompts/tools/todo-write.md +2 -2
- package/src/prompts/tools/web-search.md +2 -2
- package/src/prompts/tools/write.md +2 -5
- package/src/sdk.ts +15 -11
- package/src/session/agent-session.ts +92 -34
- package/src/session/auth-storage.ts +2 -1
- package/src/session/blob-store.ts +105 -0
- package/src/session/session-manager.ts +107 -44
- package/src/task/executor.ts +19 -9
- package/src/task/render.ts +80 -58
- package/src/tools/ask.ts +28 -5
- package/src/tools/bash.ts +47 -39
- package/src/tools/browser.ts +248 -26
- package/src/tools/calculator.ts +42 -23
- package/src/tools/fetch.ts +33 -16
- package/src/tools/find.ts +57 -22
- package/src/tools/grep.ts +54 -25
- package/src/tools/index.ts +5 -5
- package/src/tools/notebook.ts +19 -6
- package/src/tools/path-utils.ts +26 -1
- package/src/tools/python.ts +20 -14
- package/src/tools/read.ts +21 -8
- package/src/tools/render-utils.ts +5 -45
- package/src/tools/ssh.ts +59 -53
- package/src/tools/submit-result.ts +2 -2
- package/src/tools/todo-write.ts +32 -14
- package/src/tools/truncate.ts +1 -1
- package/src/tools/write.ts +42 -26
- package/src/tui/code-cell.ts +1 -1
- package/src/tui/output-block.ts +61 -3
- package/src/tui/tree-list.ts +4 -4
- package/src/tui/utils.ts +71 -1
- package/src/utils/frontmatter.ts +1 -1
- package/src/utils/title-generator.ts +1 -1
- package/src/utils/tools-manager.ts +18 -2
- package/src/web/scrapers/osv.ts +4 -1
- package/src/web/scrapers/youtube.ts +1 -1
- package/src/web/search/index.ts +1 -1
- package/src/web/search/render.ts +96 -90
|
@@ -269,6 +269,12 @@ async function installPythonPackage(pkg: string, signal?: AbortSignal): Promise<
|
|
|
269
269
|
return false;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
// Termux package names for tools
|
|
273
|
+
const TERMUX_PACKAGES: Partial<Record<ToolName, string>> = {
|
|
274
|
+
sd: "sd",
|
|
275
|
+
sg: "ast-grep",
|
|
276
|
+
};
|
|
277
|
+
|
|
272
278
|
// Ensure a tool is available, downloading if necessary
|
|
273
279
|
// Returns the path to the tool, or null if unavailable
|
|
274
280
|
type EnsureToolOptions = {
|
|
@@ -284,13 +290,23 @@ export async function ensureTool(tool: ToolName, silentOrOptions?: EnsureToolOpt
|
|
|
284
290
|
return existingPath;
|
|
285
291
|
}
|
|
286
292
|
|
|
293
|
+
// On Android/Termux, Linux binaries don't work due to Bionic libc incompatibility.
|
|
294
|
+
// Users must install via pkg.
|
|
295
|
+
if (os.platform() === "android") {
|
|
296
|
+
const pkgName = TERMUX_PACKAGES[tool] ?? tool;
|
|
297
|
+
if (!silent) {
|
|
298
|
+
logger.warn(`${TOOLS[tool]?.name ?? tool} not found. Install with: pkg install ${pkgName}`);
|
|
299
|
+
}
|
|
300
|
+
return undefined;
|
|
301
|
+
}
|
|
302
|
+
|
|
287
303
|
// Handle Python tools
|
|
288
304
|
const pythonConfig = PYTHON_TOOLS[tool];
|
|
289
305
|
if (pythonConfig) {
|
|
290
306
|
if (!silent) {
|
|
291
307
|
logger.debug(`${pythonConfig.name} not found. Installing via uv/pip...`);
|
|
292
308
|
}
|
|
293
|
-
notify?.(`Installing ${pythonConfig.name}
|
|
309
|
+
notify?.(`Installing ${pythonConfig.name}…`);
|
|
294
310
|
const success = await installPythonPackage(pythonConfig.package, signal);
|
|
295
311
|
if (success) {
|
|
296
312
|
// Re-check for the command after installation
|
|
@@ -315,7 +331,7 @@ export async function ensureTool(tool: ToolName, silentOrOptions?: EnsureToolOpt
|
|
|
315
331
|
if (!silent) {
|
|
316
332
|
logger.debug(`${config.name} not found. Downloading...`);
|
|
317
333
|
}
|
|
318
|
-
notify?.(`Downloading ${config.name}
|
|
334
|
+
notify?.(`Downloading ${config.name}…`);
|
|
319
335
|
|
|
320
336
|
try {
|
|
321
337
|
const path = await downloadTool(tool, signal);
|
package/src/web/scrapers/osv.ts
CHANGED
|
@@ -145,8 +145,11 @@ export const handleOsv: SpecialHandler = async (
|
|
|
145
145
|
if (affected.versions?.length) {
|
|
146
146
|
const versions =
|
|
147
147
|
affected.versions.length > 10
|
|
148
|
-
? `${affected.versions.slice(0, 10).join(", ")}
|
|
148
|
+
? `${affected.versions.slice(0, 10).join(", ")}… (${affected.versions.length} total)`
|
|
149
149
|
: affected.versions.join(", ");
|
|
150
|
+
affected.versions.length > 10
|
|
151
|
+
? `${affected.versions.slice(0, 10).join(", ")}… (${affected.versions.length} total)`
|
|
152
|
+
: affected.versions.join(", ");
|
|
150
153
|
md += `- **Versions:** ${versions}\n`;
|
|
151
154
|
}
|
|
152
155
|
|
|
@@ -287,7 +287,7 @@ export const handleYouTube: SpecialHandler = async (
|
|
|
287
287
|
|
|
288
288
|
if (description) {
|
|
289
289
|
// Truncate long descriptions
|
|
290
|
-
const descPreview = description.length > 1000 ? `${description.slice(0, 1000)}
|
|
290
|
+
const descPreview = description.length > 1000 ? `${description.slice(0, 1000)}…` : description;
|
|
291
291
|
md += `---\n\n## Description\n\n${descPreview}\n\n`;
|
|
292
292
|
}
|
|
293
293
|
|
package/src/web/search/index.ts
CHANGED
|
@@ -79,7 +79,7 @@ function formatProviderError(error: unknown, provider: SearchProvider): string {
|
|
|
79
79
|
/** Truncate text for tool output */
|
|
80
80
|
function truncateText(text: string, maxLen: number): string {
|
|
81
81
|
if (text.length <= maxLen) return text;
|
|
82
|
-
return `${text.slice(0, Math.max(0, maxLen -
|
|
82
|
+
return `${text.slice(0, Math.max(0, maxLen - 1))}…`;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
function formatCount(label: string, count: number): string {
|
package/src/web/search/render.ts
CHANGED
|
@@ -21,7 +21,8 @@ import {
|
|
|
21
21
|
TRUNCATE_LENGTHS,
|
|
22
22
|
truncateToWidth,
|
|
23
23
|
} from "../../tools/render-utils";
|
|
24
|
-
import {
|
|
24
|
+
import { renderStatusLine, renderTreeList } from "../../tui";
|
|
25
|
+
import { CachedOutputBlock } from "../../tui/output-block";
|
|
25
26
|
import type { SearchResponse } from "./types";
|
|
26
27
|
|
|
27
28
|
const MAX_COLLAPSED_ANSWER_LINES = PREVIEW_LIMITS.COLLAPSED_LINES;
|
|
@@ -79,7 +80,6 @@ export function renderSearchResult(
|
|
|
79
80
|
maxAnswerLines?: number;
|
|
80
81
|
},
|
|
81
82
|
): Component {
|
|
82
|
-
const { expanded } = options;
|
|
83
83
|
const details = result.details;
|
|
84
84
|
|
|
85
85
|
// Handle error case
|
|
@@ -90,7 +90,7 @@ export function renderSearchResult(
|
|
|
90
90
|
const rawText = result.content?.find(block => block.type === "text")?.text?.trim() ?? "";
|
|
91
91
|
const response = details?.response;
|
|
92
92
|
if (!response) {
|
|
93
|
-
return renderFallbackText(rawText, expanded, theme);
|
|
93
|
+
return renderFallbackText(rawText, options.expanded, theme);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const sources = Array.isArray(response.sources) ? response.sources : [];
|
|
@@ -112,12 +112,6 @@ export function renderSearchResult(
|
|
|
112
112
|
.map(l => l.trim())
|
|
113
113
|
: [];
|
|
114
114
|
const totalAnswerLines = answerLines.length;
|
|
115
|
-
const answerLimit = expanded ? MAX_EXPANDED_ANSWER_LINES : MAX_COLLAPSED_ANSWER_LINES;
|
|
116
|
-
const answerPreview = contentText
|
|
117
|
-
? args?.allowLongAnswer
|
|
118
|
-
? answerLines.slice(0, args.maxAnswerLines ?? answerLines.length)
|
|
119
|
-
: getPreviewLines(contentText, answerLimit, MAX_ANSWER_LINE_LEN)
|
|
120
|
-
: [];
|
|
121
115
|
|
|
122
116
|
const providerLabel = provider !== "none" ? getSearchProvider(provider).label : "None";
|
|
123
117
|
const queryPreview = args?.query
|
|
@@ -135,46 +129,6 @@ export function renderSearchResult(
|
|
|
135
129
|
theme,
|
|
136
130
|
);
|
|
137
131
|
|
|
138
|
-
const remainingAnswer = totalAnswerLines - answerPreview.length;
|
|
139
|
-
|
|
140
|
-
const sourceTree = renderTreeList(
|
|
141
|
-
{
|
|
142
|
-
items: sources,
|
|
143
|
-
expanded,
|
|
144
|
-
maxCollapsed: MAX_COLLAPSED_ITEMS,
|
|
145
|
-
itemType: "source",
|
|
146
|
-
renderItem: src => {
|
|
147
|
-
const titleText =
|
|
148
|
-
typeof src.title === "string" && src.title.trim()
|
|
149
|
-
? src.title
|
|
150
|
-
: typeof src.url === "string" && src.url.trim()
|
|
151
|
-
? src.url
|
|
152
|
-
: "Untitled";
|
|
153
|
-
const title = truncateToWidth(titleText, 70);
|
|
154
|
-
const url = typeof src.url === "string" ? src.url : "";
|
|
155
|
-
const domain = url ? getDomain(url) : "";
|
|
156
|
-
const age = formatAge(src.ageSeconds) || (typeof src.publishedDate === "string" ? src.publishedDate : "");
|
|
157
|
-
const metaParts: string[] = [];
|
|
158
|
-
if (domain) metaParts.push(theme.fg("dim", `(${domain})`));
|
|
159
|
-
if (typeof src.author === "string" && src.author.trim()) metaParts.push(theme.fg("muted", src.author));
|
|
160
|
-
if (age) metaParts.push(theme.fg("muted", age));
|
|
161
|
-
const metaSep = theme.fg("dim", theme.sep.dot);
|
|
162
|
-
const metaSuffix = metaParts.length > 0 ? ` ${metaParts.join(metaSep)}` : "";
|
|
163
|
-
const lines: string[] = [`${theme.fg("accent", title)}${metaSuffix}`];
|
|
164
|
-
const snippetText = typeof src.snippet === "string" ? src.snippet : "";
|
|
165
|
-
if (snippetText.trim()) {
|
|
166
|
-
const snippetLines = getPreviewLines(snippetText, MAX_SNIPPET_LINES, MAX_SNIPPET_LINE_LEN);
|
|
167
|
-
for (const snippetLine of snippetLines) {
|
|
168
|
-
lines.push(theme.fg("muted", `${theme.format.dash} ${snippetLine}`));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
if (url) lines.push(theme.fg("mdLinkUrl", url));
|
|
172
|
-
return lines;
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
theme,
|
|
176
|
-
);
|
|
177
|
-
|
|
178
132
|
const metaLines: string[] = [];
|
|
179
133
|
metaLines.push(`${theme.fg("muted", "Provider:")} ${theme.fg("text", providerLabel)}`);
|
|
180
134
|
if (response.model) metaLines.push(`${theme.fg("muted", "Model:")} ${theme.fg("text", response.model)}`);
|
|
@@ -202,9 +156,94 @@ export function renderSearchResult(
|
|
|
202
156
|
metaLines.push(`${theme.fg("muted", "Queries:")} ${theme.fg("text", queryList.join("; "))}${suffix}`);
|
|
203
157
|
}
|
|
204
158
|
|
|
159
|
+
const outputBlock = new CachedOutputBlock();
|
|
160
|
+
|
|
205
161
|
return {
|
|
206
|
-
render
|
|
207
|
-
|
|
162
|
+
render(width: number): string[] {
|
|
163
|
+
// Read mutable state at render time
|
|
164
|
+
const { expanded } = options;
|
|
165
|
+
|
|
166
|
+
// Expanded-dependent computations
|
|
167
|
+
const answerLimit = expanded ? MAX_EXPANDED_ANSWER_LINES : MAX_COLLAPSED_ANSWER_LINES;
|
|
168
|
+
const answerPreview = contentText
|
|
169
|
+
? args?.allowLongAnswer
|
|
170
|
+
? answerLines.slice(0, args.maxAnswerLines ?? answerLines.length)
|
|
171
|
+
: getPreviewLines(contentText, answerLimit, MAX_ANSWER_LINE_LEN)
|
|
172
|
+
: [];
|
|
173
|
+
const remainingAnswer = totalAnswerLines - answerPreview.length;
|
|
174
|
+
|
|
175
|
+
const sourceTree = renderTreeList(
|
|
176
|
+
{
|
|
177
|
+
items: sources,
|
|
178
|
+
expanded,
|
|
179
|
+
maxCollapsed: MAX_COLLAPSED_ITEMS,
|
|
180
|
+
itemType: "source",
|
|
181
|
+
renderItem: src => {
|
|
182
|
+
const titleText =
|
|
183
|
+
typeof src.title === "string" && src.title.trim()
|
|
184
|
+
? src.title
|
|
185
|
+
: typeof src.url === "string" && src.url.trim()
|
|
186
|
+
? src.url
|
|
187
|
+
: "Untitled";
|
|
188
|
+
const title = truncateToWidth(titleText, 70);
|
|
189
|
+
const url = typeof src.url === "string" ? src.url : "";
|
|
190
|
+
const domain = url ? getDomain(url) : "";
|
|
191
|
+
const age =
|
|
192
|
+
formatAge(src.ageSeconds) || (typeof src.publishedDate === "string" ? src.publishedDate : "");
|
|
193
|
+
const metaParts: string[] = [];
|
|
194
|
+
if (domain) metaParts.push(theme.fg("dim", `(${domain})`));
|
|
195
|
+
if (typeof src.author === "string" && src.author.trim())
|
|
196
|
+
metaParts.push(theme.fg("muted", src.author));
|
|
197
|
+
if (age) metaParts.push(theme.fg("muted", age));
|
|
198
|
+
const metaSep = theme.fg("dim", theme.sep.dot);
|
|
199
|
+
const metaSuffix = metaParts.length > 0 ? ` ${metaParts.join(metaSep)}` : "";
|
|
200
|
+
const srcLines: string[] = [`${theme.fg("accent", title)}${metaSuffix}`];
|
|
201
|
+
const snippetText = typeof src.snippet === "string" ? src.snippet : "";
|
|
202
|
+
if (snippetText.trim()) {
|
|
203
|
+
const snippetLines = getPreviewLines(snippetText, MAX_SNIPPET_LINES, MAX_SNIPPET_LINE_LEN);
|
|
204
|
+
for (const snippetLine of snippetLines) {
|
|
205
|
+
srcLines.push(theme.fg("muted", `${theme.format.dash} ${snippetLine}`));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (url) srcLines.push(theme.fg("mdLinkUrl", url));
|
|
209
|
+
return srcLines;
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
theme,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// Build answer section
|
|
216
|
+
const answerState = sourceCount > 0 ? "success" : "warning";
|
|
217
|
+
const borderColor: "warning" | "dim" = answerState === "warning" ? "warning" : "dim";
|
|
218
|
+
const border = (t: string) => theme.fg(borderColor, t);
|
|
219
|
+
const contentPrefix = border(`${theme.boxSharp.vertical} `);
|
|
220
|
+
const contentSuffix = border(theme.boxSharp.vertical);
|
|
221
|
+
const contentWidth = Math.max(0, width - visibleWidth(contentPrefix) - visibleWidth(contentSuffix));
|
|
222
|
+
const answerTreeLines = answerPreview.length > 0 ? answerPreview : ["No answer text returned"];
|
|
223
|
+
const answerTree = renderTreeList(
|
|
224
|
+
{
|
|
225
|
+
items: answerTreeLines,
|
|
226
|
+
expanded: true,
|
|
227
|
+
maxCollapsed: answerTreeLines.length,
|
|
228
|
+
itemType: "line",
|
|
229
|
+
renderItem: (line, context) => {
|
|
230
|
+
const coloredLine =
|
|
231
|
+
line === "No answer text returned" ? theme.fg("muted", line) : theme.fg("dim", line);
|
|
232
|
+
if (!args?.allowLongAnswer) {
|
|
233
|
+
return coloredLine;
|
|
234
|
+
}
|
|
235
|
+
const prefixWidth = visibleWidth(context.continuePrefix);
|
|
236
|
+
const wrapWidth = Math.max(10, contentWidth - prefixWidth);
|
|
237
|
+
return wrapTextWithAnsi(coloredLine, wrapWidth);
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
theme,
|
|
241
|
+
);
|
|
242
|
+
if (remainingAnswer > 0) {
|
|
243
|
+
answerTree.push(theme.fg("muted", formatMoreItems(remainingAnswer, "line")));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return outputBlock.render(
|
|
208
247
|
{
|
|
209
248
|
header,
|
|
210
249
|
state: sourceCount > 0 ? "success" : "warning",
|
|
@@ -218,43 +257,7 @@ export function renderSearchResult(
|
|
|
218
257
|
: []),
|
|
219
258
|
{
|
|
220
259
|
label: theme.fg("toolTitle", "Answer"),
|
|
221
|
-
lines:
|
|
222
|
-
const state = sourceCount > 0 ? "success" : "warning";
|
|
223
|
-
const borderColor: "warning" | "dim" = state === "warning" ? "warning" : "dim";
|
|
224
|
-
const border = (text: string) => theme.fg(borderColor, text);
|
|
225
|
-
const contentPrefix = border(`${theme.boxSharp.vertical} `);
|
|
226
|
-
const contentSuffix = border(theme.boxSharp.vertical);
|
|
227
|
-
const contentWidth = Math.max(
|
|
228
|
-
0,
|
|
229
|
-
width - visibleWidth(contentPrefix) - visibleWidth(contentSuffix),
|
|
230
|
-
);
|
|
231
|
-
const answerTreeLines = answerPreview.length > 0 ? answerPreview : ["No answer text returned"];
|
|
232
|
-
const answerTree = renderTreeList(
|
|
233
|
-
{
|
|
234
|
-
items: answerTreeLines,
|
|
235
|
-
expanded: true,
|
|
236
|
-
maxCollapsed: answerTreeLines.length,
|
|
237
|
-
itemType: "line",
|
|
238
|
-
renderItem: (line, context) => {
|
|
239
|
-
const coloredLine =
|
|
240
|
-
line === "No answer text returned"
|
|
241
|
-
? theme.fg("muted", line)
|
|
242
|
-
: theme.fg("dim", line);
|
|
243
|
-
if (!args?.allowLongAnswer) {
|
|
244
|
-
return coloredLine;
|
|
245
|
-
}
|
|
246
|
-
const prefixWidth = visibleWidth(context.continuePrefix);
|
|
247
|
-
const wrapWidth = Math.max(10, contentWidth - prefixWidth);
|
|
248
|
-
return wrapTextWithAnsi(coloredLine, wrapWidth);
|
|
249
|
-
},
|
|
250
|
-
},
|
|
251
|
-
theme,
|
|
252
|
-
);
|
|
253
|
-
if (remainingAnswer > 0) {
|
|
254
|
-
answerTree.push(theme.fg("muted", formatMoreItems(remainingAnswer, "line")));
|
|
255
|
-
}
|
|
256
|
-
return answerTree;
|
|
257
|
-
})(),
|
|
260
|
+
lines: answerTree,
|
|
258
261
|
},
|
|
259
262
|
{
|
|
260
263
|
label: theme.fg("toolTitle", "Sources"),
|
|
@@ -265,8 +268,11 @@ export function renderSearchResult(
|
|
|
265
268
|
width,
|
|
266
269
|
},
|
|
267
270
|
theme,
|
|
268
|
-
)
|
|
269
|
-
|
|
271
|
+
);
|
|
272
|
+
},
|
|
273
|
+
invalidate() {
|
|
274
|
+
outputBlock.invalidate();
|
|
275
|
+
},
|
|
270
276
|
};
|
|
271
277
|
}
|
|
272
278
|
|