@thoth-agents/pi-thoth-theme 0.3.0 → 0.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.
Files changed (3) hide show
  1. package/README.md +4 -2
  2. package/dist/index.ts +187 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -37,8 +37,10 @@ of the module toggles below.
37
37
  drop speed, then cache, then tokens before truncating cost. Editor-border
38
38
  metadata is decorated in place without claiming the custom editor slot.
39
39
  - **Tools**: boxed calls and results for `read`, `bash`, `powershell`, `ls`,
40
- `grep`, `find`, `edit` and `write`, with Nerd Font icons, native Unicode
41
- glyphs or ASCII alternatives. `bash` and `powershell` show a live elapsed time
40
+ `grep`, `find`, `edit`, `write` and `codemode`, with Nerd Font icons, native Unicode
41
+ glyphs or ASCII alternatives. `codemode` previews up to eight nested calls
42
+ with status and duration; expanded results show the script, output and errors
43
+ without replacing Pi's builtin codemode extension. `bash` and `powershell` show a live elapsed time
42
44
  while running. Execution and parameters delegate to Pi's built-in tools;
43
45
  Pi's native output toggle still
44
46
  collapses and expands results. Registered non-built-in tools keep their own
package/dist/index.ts CHANGED
@@ -54,6 +54,15 @@ function getToolElapsedMs(options) {
54
54
  state.completedElapsedMs = elapsedMs;
55
55
  return elapsedMs;
56
56
  }
57
+ function renderToolFooter(kit, theme, options) {
58
+ if (kit?.toolFooter) return kit.toolFooter(theme, options);
59
+ const elapsedMs = getToolElapsedMs(options);
60
+ const elapsed = elapsedMs === void 0 ? "" : formatDuration(Math.floor(elapsedMs / 1e3) * 1e3);
61
+ const terminalGlyph = terminalFooterGlyphs[options.status];
62
+ const status = terminalGlyph ?? (options.status === "in_progress" ? "running" : options.status);
63
+ const summary = typeof options.summary === "string" ? [options.summary] : options.summary ?? [];
64
+ return [status, elapsed, ...terminalGlyph ? summary : []].filter(Boolean).join(" \xB7 ");
65
+ }
57
66
  function registration() {
58
67
  try {
59
68
  const record = shared[registryKey];
@@ -71,6 +80,41 @@ function registerRenderKit(kit, owner) {
71
80
  function withdrawRenderKit(token) {
72
81
  if (registration()?.token === token) delete shared[registryKey];
73
82
  }
83
+ function getRenderKit() {
84
+ try {
85
+ const kit = registration()?.kit;
86
+ if (kit?.version === RENDER_KIT_VERSION && (!("resolveToolRenderers" in kit) || typeof kit.resolveToolRenderers === "function") && (!("toolFooter" in kit) || typeof kit.toolFooter === "function") && (!("icon" in kit) || typeof kit.icon === "function") && [
87
+ "card",
88
+ "collapse",
89
+ "cachedComponent",
90
+ "indicator",
91
+ "statusGlyph",
92
+ "widgetHeading",
93
+ "treeRow",
94
+ "fg"
95
+ ].every(
96
+ (method2) => typeof kit[method2] === "function"
97
+ ))
98
+ return kit;
99
+ } catch {
100
+ }
101
+ return void 0;
102
+ }
103
+ function createKitRenderMemo() {
104
+ let cached;
105
+ return {
106
+ render(width, build) {
107
+ const kit = getRenderKit();
108
+ if (!cached || cached.width !== width || cached.kit !== kit) {
109
+ cached = { width, kit, lines: build(kit) };
110
+ }
111
+ return cached.lines;
112
+ },
113
+ invalidate() {
114
+ cached = void 0;
115
+ }
116
+ };
117
+ }
74
118
  var nativeFrames, terminalFooterGlyphs, RENDER_KIT_VERSION, registryKey, shared;
75
119
  var init_render_kit = __esm({
76
120
  "../pi-core/src/render-kit.ts"() {
@@ -2254,6 +2298,148 @@ function createCustomPowerShellTool(_cwd, config) {
2254
2298
  return createCustomShellTool(POWERSHELL_CONFIG, config);
2255
2299
  }
2256
2300
 
2301
+ // src/tools/codemode.ts
2302
+ var COLLAPSED_CALLS = 8;
2303
+ var RESULT_PRESENT = "codemodeResultPresent";
2304
+ var RESULT_BORDER = "codemodeResultBorder";
2305
+ var CALL_COMPONENT = "codemodeCallComponent";
2306
+ var CALL_STATUS = {
2307
+ running: "running",
2308
+ ok: "completed",
2309
+ error: "failed",
2310
+ cancelled: "cancelled"
2311
+ };
2312
+ function displayLines(text) {
2313
+ return text.split("\n").map(escapeControlCharacters);
2314
+ }
2315
+ function scriptText(args) {
2316
+ return args && typeof args === "object" && "code" in args && typeof args.code === "string" ? args.code : "(script unavailable)";
2317
+ }
2318
+ function displayCalls(details) {
2319
+ if (!details || typeof details !== "object" || !("calls" in details) || !Array.isArray(details.calls))
2320
+ return [];
2321
+ return details.calls.flatMap((call) => {
2322
+ if (!call || typeof call !== "object" || !("name" in call) || typeof call.name !== "string" || !("status" in call) || call.status !== "running" && call.status !== "ok" && call.status !== "error" && call.status !== "cancelled")
2323
+ return [];
2324
+ return [
2325
+ {
2326
+ name: call.name,
2327
+ status: call.status,
2328
+ durationMs: "durationMs" in call && typeof call.durationMs === "number" && Number.isFinite(call.durationMs) && call.durationMs >= 0 ? call.durationMs : void 0,
2329
+ error: "error" in call && typeof call.error === "string" ? call.error : void 0
2330
+ }
2331
+ ];
2332
+ });
2333
+ }
2334
+ function createCodemodeRenderer(config) {
2335
+ const fallbackKit = createRenderKit({}, void 0, config.icons);
2336
+ return {
2337
+ renderShell: "self",
2338
+ renderCall(_args, theme, context) {
2339
+ if (context?.state) {
2340
+ context.state[RESULT_PRESENT] = false;
2341
+ context.state[RESULT_BORDER] = void 0;
2342
+ }
2343
+ const memo = createKitRenderMemo();
2344
+ const component = {
2345
+ invalidate: () => memo.invalidate(),
2346
+ render(width) {
2347
+ return memo.render(width, (registeredKit) => {
2348
+ const kit = registeredKit ?? fallbackKit;
2349
+ const hasResult = context?.state?.[RESULT_PRESENT] === true;
2350
+ const status = context?.executionStarted ? "running" : "pending";
2351
+ return kit.card(
2352
+ theme,
2353
+ {
2354
+ title: theme.fg("toolTitle", "codemode"),
2355
+ footer: hasResult ? "" : renderToolFooter(kit, theme, { status, context }),
2356
+ part: hasResult ? "start" : "full",
2357
+ ...context?.state?.[RESULT_BORDER]
2358
+ },
2359
+ width
2360
+ );
2361
+ });
2362
+ }
2363
+ };
2364
+ if (context?.state) context.state[CALL_COMPONENT] = component;
2365
+ return component;
2366
+ },
2367
+ renderResult(result, options, theme, context) {
2368
+ const status = options.isPartial ? "running" : context?.isError ? "failed" : "completed";
2369
+ const border = {
2370
+ isError: status === "failed",
2371
+ isSuccess: status === "completed" && context?.executionStarted !== false
2372
+ };
2373
+ if (context?.state) {
2374
+ context.state[RESULT_PRESENT] = true;
2375
+ context.state[RESULT_BORDER] = border;
2376
+ context.state[CALL_COMPONENT]?.invalidate?.();
2377
+ }
2378
+ const calls = displayCalls(result.details);
2379
+ const visibleCalls = options.expanded ? calls : calls.slice(0, COLLAPSED_CALLS);
2380
+ const output = displayLines(getResultText(result) || "(no output)");
2381
+ const errors = calls.flatMap(
2382
+ (call) => call.error ? displayLines(`${call.name}: ${call.error}`).map(
2383
+ (line) => theme.fg("error", line)
2384
+ ) : []
2385
+ );
2386
+ const memo = createKitRenderMemo();
2387
+ return {
2388
+ invalidate: () => memo.invalidate(),
2389
+ render(width) {
2390
+ return memo.render(width, (registeredKit) => {
2391
+ const kit = registeredKit ?? fallbackKit;
2392
+ return kit.card(
2393
+ theme,
2394
+ {
2395
+ part: context?.state?.[CALL_COMPONENT] ? "end" : "full",
2396
+ title: theme.fg("toolTitle", "codemode"),
2397
+ body: (bodyWidth) => {
2398
+ if (calls.length === 0) {
2399
+ return options.expanded ? [] : kit.collapse(theme, output, options);
2400
+ }
2401
+ const rows = visibleCalls.map((call, i) => {
2402
+ const duration = call.durationMs === void 0 ? "" : ` \xB7 ${formatDuration(call.durationMs)}`;
2403
+ return kit.treeRow(
2404
+ theme,
2405
+ {
2406
+ text: `${kit.statusGlyph(theme, CALL_STATUS[call.status])} ${escapeControlCharacters(call.name)}${duration}`,
2407
+ last: i === visibleCalls.length - 1
2408
+ },
2409
+ bodyWidth
2410
+ );
2411
+ });
2412
+ if (!options.expanded && calls.length > COLLAPSED_CALLS) {
2413
+ rows.push(
2414
+ theme.fg(
2415
+ "dim",
2416
+ `+${calls.length - COLLAPSED_CALLS} more \xB7 ctrl+o to expand`
2417
+ )
2418
+ );
2419
+ }
2420
+ return rows;
2421
+ },
2422
+ sections: options.expanded ? [
2423
+ {
2424
+ title: "Script",
2425
+ rows: displayLines(scriptText(context?.args))
2426
+ },
2427
+ { title: "Output", rows: output },
2428
+ ...errors.length ? [{ title: "Errors", rows: errors }] : []
2429
+ ] : void 0,
2430
+ wrap: options.expanded,
2431
+ footer: renderToolFooter(kit, theme, { status, context }),
2432
+ ...border
2433
+ },
2434
+ width
2435
+ );
2436
+ });
2437
+ }
2438
+ };
2439
+ }
2440
+ };
2441
+ }
2442
+
2257
2443
  // src/tools/edit.ts
2258
2444
  var COLLAPSED_DIFF_LINES = 6;
2259
2445
  function parseDiffStats(diffText) {
@@ -3345,6 +3531,7 @@ function createCustomWriteTool(cwd, config) {
3345
3531
  // src/tools/index.ts
3346
3532
  function createToolRendererResolver(pi, config, cwd = process.cwd()) {
3347
3533
  const renderers = /* @__PURE__ */ new Map([
3534
+ ["codemode", createCodemodeRenderer(config)],
3348
3535
  ["read", createCustomReadTool(cwd, config)],
3349
3536
  ["bash", createCustomBashTool(cwd, config)],
3350
3537
  ["powershell", createCustomPowerShellTool(cwd, config)],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thoth-agents/pi-thoth-theme",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "Modular Thoth theme, status line, tool styling, and welcome header for Pi.",
6
6
  "license": "MIT",